二分查找:
1. 基本二分查找
给一个数组,已升序排序,即不存在重复元素,查找给定值target,如果不存在,返回该值在数组中可以插入的位置。二分查找本质是利用分治加剪枝不断进行问题规模的缩小,到最后问题不可分解决问题。将一个区间分为两半(缩小规模),分别查找,因为另一个子问题肯定无解,不需要查找(剪枝),所以本质是剪枝的分治。另外对二分查找循环不变式分析的过程要按查找值存在与否分类讨论,且随着迭代的深入,区间大小始终是在减半收缩。
1.1. 流程
1)将查找区间一分为二,选取中间点,若是要查找元素,返回
2)如果中间点小于target值,查找右半区间,从 mid + 1 到 right
3)若中间点大于target值,查找左半区间,从 left 到 mid - 1
4)如果区间不存在,即left > right ,结束。
1.2. 朴素实现
int binary_search(int *arr, int size, int target) {
int l = 0, r = size - 1;
while (l <= r) {
int mid = l + (r-l) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] > target) {
r = mid - 1;
} else {
l = mid + 1;
}
}
return l;
}
1.3. 分析
1.3.1 循环条件解释
因为循环的本质是在进行区间的收缩,因为对于一个target来说,肯定要么在左半区间,要么在右半区间,或者刚好在区间中间点(会直接返回),其他情况都需要不断查找target在哪个半区间,而区间最小的情况是只有一个元素,即 [ l e f t , l e f t ] [left, left] [left,left]或者 [ r i g h t , r i g h t ] [right,right] [right,right]。
1.3.2 循环不变式
循环不变式分为两种情况讨论,如果target在数组中,如果target不在数组中
1. target在数组中
target一定在区间 [ l e f t , r i g h t ] [left, right] [left,right]中。
- 初始情况
target值一定在区间 [ l e f t , r i g h t ] [left,right] [left,right]中。 - 迭代
将区间一分为二,如果mid所指值小于target,因为target存在,那么target一定在 [ m i d + 1 , r i g h t ] [mid + 1, right] [mid+1,right]右半区间,如果mid所指的值大于target,同理target的值一定在 [ l e f t , m i d − 1 ] [left, mid - 1] [left,mid−1]的左半区间,通过不断的收缩区间大小为原来一半,不断缩小搜索范围。 - 终止
可以知道最终left会等于right,此时由不变式分析,target一定在 [ l e f t , r i h g t ] [left,rihgt] [left,rihgt]区间,此时区间只有一个数一定是target。
2. target不在数组
循环不变式: 如果要插入target则一定插入在区间中的某个位置,要么就是插入在left,或者插入在right之后。
-
初始情况
初始情况右三种:- I. target在数组下标left之前
此时区间中所有值均大于target,target插入必定插入在left - II. target在数组right之后
此时target大于数组中所有值,插入一定插入在right之后 - III. target在数组中某两个数之间 [ i , i + 1 ] [i, i + 1] [i,i+1]
此时插入一定是插入在数组中间
- I. target在数组下标left之前
-
迭代
- I. target在数组left之前
此时区间中所有的数均大于target,所以一直查找 [ l e f t , m i d − 1 ] [left, mid - 1] [left,mid−1]左半区间。left始终不变,插入位置在left。 - II. target在right之后
此时区间中所有数都小于target,一直查找 [ m i d + 1 , r i g h t ] [mid + 1, right] [mid+1,right]右半区间,right不变,插入位置在right之后。 - III. target在数组中某两个数之间 [ i , i + 1 ] [i,i+1] [i,i+1]
此时分类:- m i d > i + 1 mid > i+1 mid>i+1
即 [ i , i + 1 ] [i,i+1] [i,
- m i d > i + 1 mid > i+1 mid>i+1
- I. target在数组left之前