前言:都说二分查找中的细节是魔鬼,所以今天前来对二分查找做一个详细的总结。
先贴出一道题供大家练手: LC 34. 在排序数组中查找元素的第一个和最后一个位置
1. 基础二分查找
int check(vector<int>& arr, int tar){
int n = arr.size();
int L = 0, R = n - 1;
while(L <= R){
int mid = (L + R) >> 1;
if(arr[mid] == tar)
return mid;
if(arr[mid] > tar)
R = mid - 1;
else
L = mid + 1;
}
return -1;
}
2. 寻找左侧边界的二分查找
int check(vector<int>& arr, int tar){
int ans = -1, n = arr.size();
int L = 0, R = n - 1;
while(L <= R){
int mid = (L + R) >> 1;
if(a[mid] == tar)
ans = mid;
if(a[mid] >= tar)
R = mid - 1;
else
L = mid + 1;
}
return ans;
}
3. 寻找右侧边界的二分查找
int check(vector<int>& arr, int tar){
int ans = -1, n = arr.size();
int L = 0, R = n - 1;
while(L <= R){
int mid = (L + R) >> 1;
if(a[mid] == tar)
ans = mid;
if(a[mid] <= tar)
L = mid + 1;
else
R = mid - 1;
}
return ans;
}
标准化
- 由后面两个代码模板可以看出,无论是左边界还是右边界,核心都是统一的:二分查找过程中的最后一次满足条件的值,即为边界,那剩下的只是 判断一下是否满足条件以及左右边界该如何移动。这里附上一个
C++
版本的标准二分查找模板。
{
int L = x, R = y, ans = 0;
while(L <= R){
int mid = (L + R) >> 1; // 看情况是否采取防爆 -> 但是不会影响结果
if(check(mid)){
ans = mid;
L = mid + 1;
}else{
R = mid - 1;
}
}
return ans;
}
另外
- 对于
C++
而言,自带二分查找的库函数,左边界:lower_bound()
,右边界:upper_bound()
,具体的使用细节请参考官方文献:lower_bound() 和 upper_bound().