目录
二分两种模板:
LeetCode暑期刷题打卡2019——Week1 二分专题_哔哩哔哩_bilibili
8:50 开始
95%二分查找的题目都满足两段性条件:
一段全都满足我们要找的条件,另一段全都不满足。
另外百分之5,不符合两段论的二分题,模板依旧是下面两个,但是check函数要设计好。
而二分法,有且仅有两种更新条件。
- r = mid, l = mid+1
- mid计算的时候,正常的向下取整即可 , 即
- mid = (l + r)/ 2
- l = mid, r = mid -1
- 这种mid计算的时候,就需要向上取整, 即
- mid = (l+r+1)/ 2
推导方法:
mid在左右区间怎么判断呢?
如果if (check(mid)) 条件成立,首先考虑一下答案是找最后一个满足要求的,还是第一个满足要求的:如果是最后一个满足要求的 check(mid)成立就要,l = mid, 如果check(mid) 不成立则r = mid -1 ,
如果是第一个满足要求的check(mid)成立就要,r = mid, 如果check(mid) 不成立则l = mid +1 ,
为什么
- l = mid, r = mid -1
- 这种mid计算的时候,就需要向上取整?, 即
- mid = (l+r+1)/ 2
- 因为考虑现在只有两个数的情况:
- l = 0, r = 1, 假如mid向下取整则mid = 0, 如果此时更新方式又是l = mid = 0, 那 l 和r的值就没变,陷入死循环了。
记忆方法:
最后一个,更新方向是反的(左), l = mid, mid更新也是反的,向上取整
第一个,更新方向是反的(右), r = mid, mid更新也是反的,向下取整
更新时,右比左小1
1)模板一(后半段的开头):
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + (r-l)/2 ;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l; // return left or right 均可,当然根据题意left也要检查一下
// 因为数组里面可能就没有要找的东西
}
2)模板二(前半段的结尾):
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l +( r-l + 1)/2 ;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
应用:实现lower_bound (找到第一个大于等于val的)
因为是找第一个,所以用模板1
// 第一个大于等于val的
int lower_bound(vector<int> & vec, int val){
int left=0,right=vec.size()-1;
while(left<right){
int mid =left+(right-left)/2;
if(vec[mid]>=val)
right=mid;
else
left=mid+1;
}
// 最后还是要检查一下的
if(vec[left]>=val)
return left;
else
return -1;
}
3)查找值相同的二分:
从一个有序数组中找到一个符合要求的精确值(如猜数游戏)。如查找值为Key的元素下标,不存在返回-1。
对于那些排序数组,找符合条件的解,一般考虑用二分查找
//二分查找 数组从小到大排列 时间复杂度 logN
找到数组arry中值等于key的下标
int binary_search(int *arry, int len, int key) {
int left = 0, mid, right = len - 1;
while (left <= right) { // 有一个范围也得找,所以是《=
mid = left + (right - left) / 2; //不能写 (left+right)/2 数组比较长可能会溢出
if (key < arry[mid]) right = mid - 1;
else if (key>arry[mid]) left = mid + 1;
else
return mid;
}
return -1;
}
查找值相同的二分小变种:找到第一个k和最后一个k的二分:
可以不看这个,因为可以用上面的两个模板实现。
假如找第一个,那就是模板1,check函数是大于等于target
假如找最后一个,那就是模板2,check函数是小于等于target
当然最后别忘了,检查一下是否满足
int getfirst_k(int *arr,int len,int k){
int left=0, right=len-1;
int mid;
while(left<=right){
mid = left+((right-left)>>1);
if(arr[mid]<k){
left=mid+1;
} else if(arr[mid]>k){
right=mid-1;
} else if(mid>0 && arr[mid-1]== k){
right=mid-1;
} else{
return mid;
}
}
return -1;
}
int getlast_k(int *arr,int len ,int k){
int left=0,right=len-1,mid;
while(left<=right){
mid=left+((right-left)>>1);
if(arr[mid]<k){
left=mid+1;
} else if(arr[mid]>k){
right=mid-1;
} else if(mid<(len-1) && arr[mid+1]==k){
left=mid+1;
} else {
return mid;
}
}
return -1;
}