二分查找
二分查找
二分查找是一个 搜索算法 ,我们需要准备一个 已排序数组 以及一个待查找的元素
通过该算法, 返回该元素的 index ,若该元素不存在于数组中, 则返回 -1
代码详解
算法准备:\
准备一个 _ 已排序数组 _ (arr) 以及一个待查找 _ 元素 _ (x)
算法思路:\
- 计算数组的中间索引,从而将数组区分为左右两个部分
- 若 数组长度为
arr.len = 10,则中间索引为mid_index = 5,那么将数组区分为arr[0..4], 与arr[6..9]两个部分
- 若 数组长度为
- 比较
x与arr[mid_index]的大小if x = arr[mid_index]那么找出的索引为 5if x < arr[mid_index]那么要找的索引在arr[0..4]部分if x > arr[mid_index]那么要找的索引在arr[6..9]部分
- 重复以上步骤,直到
- 找到元素索引
- min_index > max_index 元素不存在于数组中
代码实现
- 伪代码
function binary_search(value,arr){
min_index = 0;
max_index = arr.len()
while min_index <= max_index{
mid_index = (min_index + max_index) / 2
if arr[mid_index] == value{
return mid_index
}
elseif value > arr[mid_index] {
min_index = mid_index + 1
}
else {
max_index = mid_index - 1
}
}
return -1;
}
- rust 实现
fn binary_search<T: PartialOrd>(arr: &[T], target: &T) -> Option<usize> {
let mut min_index = 0;
let mut max_index = arr.len() -1;
while min_index <= max_index {
let mid_index = (min_index + max_index) / 2;
if arr[mid_index] > *target {
max_index = mid_index - 1;
}
else if arr[mid_index] < *target {
min_index = mid_index + 1;
} else {
return Some(mid_index);
}
}
None
}
算法分析
最优的算法复杂度应该是 $O(1)$
平均算法复杂度应该是
他一直是 /2 /2 /2
所以说这个东西是如何索引的
二分查找的变种
以上的算法对只有一个相同的数值的时候是有效的,但如果存在多个相同的数组呢?如 [1,1,2,2,3,3,3,3,5,5]
针对上述情况,我们可以提出很多问题,如:
- 查找是否存在数组中元素是否存在
- 查找第一个等于给定值的元素
- 查找最后一个等于给定值的元素
- 查找第一个大于给定值的元素
- 查找最后一个小于给定值的元素
面对上述问题,上文给出的算法就不太行了,所以我们需要针对算法进行改进
// 查找数组(arr)中是否存在元素(target)
fn contains(arr,target){
let min_index = 0;
let max_index = arr.len() - 1;
while min_index <= max_index {
let mid_index = (min_index + max_index) / 2;
if arr[mid_index] > target {
max_index = mid_index - 1;
}
else if arr[mid_index] < target {
min_index = mid_index + 1;
} else {
return true
}
}
return false
}
// 查找第一个等于给定值的元素
fn first(arr,target){
// 当返回值为 -1 的时候, 表明并没有找到该元素
let ans = -1;
let min_index = 0;
let max_index = arr.len() - 1;
while min_index <= max_index {
let mid_index = (min_index + max_index) / 2;
if arr[mid_index] > target {
// 如果 arr[mid_index] > target 那么就说明我们要找的元素在 [min_index,mid_index-1] 中
// 下文同理
max_index = mid_index - 1;
}else if arr[mid_index] < target{
min_index = mid_index + 1;
}else{
// 因为要找第一个值,所以要找的范围在 [min_index,mid_index] 中
//
ans = mid_index
max_index = mid_index - 1;
}
}
return ans
}
// 变种三和变种二思路相似,这里就不再赘述了
...
// 查找第一个大于给定值的元素
fn leastgreater(arr,target){
let ans = -1;
let min_index = 0;
let max_index = arr.len() - 1;
while min_index <= max_index {
let mid_index = (min_index + max_index) / 2;
if arr[mid_index] < target{
min_index = mid_index + 1;
}else if arr[mid_index] > target{
ans = mid_index;
max_index = mid_index - 1;
}else{
min_index = mid_index + 1;
}
}
return ans
}
单边二分查找解析
二分查找的另外一个变种就是 单边二分查找 也叫 元二分查找
与 二分查找不同的是,
在 使用场景 上
单边二分查找 只在一侧进行查找,通常用于特定的应用场景。
比如说大量的数据需要快速查找的情况下,或者当 比较 操作花费比较高的情况下
在 算法模型 上
单边二分查找并不循环比较大小,而是通过 一定的间隔 进行查找,通常设定为 2
算法步骤
首先我们设定间隔为 2,也就是通过每次跳过 2 个元素来进行查找,直到找到目标元素或超出数组范围。
因为设定间隔为2,所以我们确定通过二进制来表示数组的索引
比如说有数组 arr [-2,-1,0,1,2,3,4,5] ,使用二进制索引,那么 3 的索引为 101,即 5 为 arr[5]
所以在数据 arr 中,其计算方法为 $log_2(n) = bits$,其中 n为数组长度 \
所以在这里用 3bits 来表示数据的索引
也就是说 arr[0] 的索引为 000,arr[1] 的索引为 001,arr[2] 的索引为 010,依次类推
所以如果要找 'valueToFind' 的索引,我们要做的事情就是确认索引的位置,假设 valueToFind=3, 那么我们需要找到索引 101 , 也就是 arr[5]
根据 arr 的长度,我们可以将索引分为 3 个部分,分别为 $x_1 x_2 x_3$,其中 $x_1$ 表示最高位,$x_2$ 表示中间位,$x_3$ 表示最低位
所以我们的问题是 要在 arr 中找到 valueToFind 的索引 根据上面的例子,我们给出具体的步骤
- 已知索引为 3bits 我们假定为 $x_1 x_2 x_3$(详情见上),我们要做的工作就是
- 确认 $x_1$ 的值
- 确认 $x_2$ 的值
- 确认 $x_3$ 的值
从而确认索引的值
- 确认 $x_1$ 的值
- 如果 $x_1 = 0$ 那么我们要找的索引在
arr[0..3]中 - 如果 $x_1 = 1$ 那么我们要找的索引在
arr[4..7]中 - 我们一般假设 $x_1 = 1$ ,也就是从
arr[4..7]中查找 - 已知 $x_1 = 1$ ,那么索引的值最小为
100, 最大为111\
我们先从最小的值开始查找,又已知valueToFind = 3,valueToFind > arr[100](arr[4])\
所以我们需要向后查找,也就是我们要找的范围为arr[100..111]据此我们可以确认 $x_1 = 1$
- 如果 $x_1 = 0$ 那么我们要找的索引在
- 确认 $x_2$ 的值
- 如果 $x_2 = 0$ 那么我们要找的索引在
arr[100..101]中 - 如果 $x_2 = 1$ 那么我们要找的索引在
arr[110..111] - 我们一般假设 $x_2 = 1$ ,也就是从
arr[110..111]中查找 - 已知 $x_2$ = 1$ ,那么索引的值最小为
110, 最大为111\
我们先从最小的值开始查找,又已知valueToFind = 3,valueToFind < arr[110](arr[6])\
所以我们需要向前查找,也就是我们要找的范围为arr[100..101]据此我们可以确认 $x_2 = 0$
- 如果 $x_2 = 0$ 那么我们要找的索引在
- 确认 $x_3$ 的值
- 如果 $x_3 = 0$ 那么我们要找的索引在
arr[100]中 - 如果 $x_3 = 1$ 那么我们要找的索引在
arr[101]中 - 我们一般假设 $x_3 = 1$ ,也就是从
arr[101]中查找 - 已知 $x_3 = 1$ ,那么索引的值最小为
101, 最大为101\
我们先从最小的值开始查找,又已知valueToFind = 3,valueToFind = arr[101](arr[5])\
所以我们找到了目标索引101据此我们可以确认 $x_3 = 1$
- 如果 $x_3 = 0$ 那么我们要找的索引在
算法实现
fn meta_binary_search<T: PartialOrd>(arr: &[T], target: &T) -> Option<usize> {
let n = arr.len();
// 当为空数组的时候返回 _None_
if n == 0 {
return None;
}
// 确认数组 _bits_ 数
let mut num_bits_for_maxindex = (n - 1).ilog2() + 1;
let mut index = 0;
// 确认边界值
if index < n && arr[index] == *target {
return Some(index);
}
while num_bits_for_maxindex > 0 {
// 按位或, 确保在不影响其他 _bits_ 的情况下更改对应位数的值
let new_index = index | 1 << (num_bits_for_maxindex - 1);
// 如果找到了就返回
if new_index < n && arr[new_index] == *target {
return Some(new_index);
} else if arr[new_index] < *target {
index = new_index
}
num_bits_for_maxindex -= 1;
}
None
}

浙公网安备 33010602011771号