本文章针对对于二分算法有一定基础但是并不算牢固的童鞋(譬如我)
二分的注意事项
做的题多了,发现怎么用二分的都有,有在判断条件上用left<=right的,也有用left<right的,有在mid<target的时候让left=mid也有让left=mid+1的,(此处数列递增排序),同理right的改变也是五花八门,最终二分结束到底得出个啥来,会产生什么问题我经常弄得很懵,今天先把这个基础的搞熟。
基础二分模板
这个模板应用于寻找x在数组中的位置(如果有的话)如果没有的话,就寻找在数组中第一个比x大的元素的位置。返回的是left,它能满足这个要求。
int bin_search(int *a,int n,int x){
//此处针对左开右闭的数组
int left=0,right=n;//其中left是数组第一个元素位置
//right是最后一个元素的后面一个位置
while(left<right){//判断条件是left!=right
int mid=(left+right)>>1;
if(a[mid]>x){
right=mid;//right直接等于mid
}else left=mid+1;//left等于mid+1
}
return left;//最后得到的left是对应x在数组中的位置或者
//第一个比x大的元素的位置
}
对这个模板的理解:
它一开始的二分由left与right组成的区间是左开右闭的,所以接下来每一次更新出来的区间都是左开右闭的,因此才有right是直接等于mid但是left是等于mid+1,同理,因为左开右闭,所以left肯定不能等于right/
唔,还有锁定目标的原理,就是因为利用了计算机除法中小数直接舍弃,在形状上表现出来的就是两个相邻的数字比如3和5如果target是4经过二分之后肯定结果是mid+1返回5的位置,因此是如果没有x的话会返回第一个比x大的位置。
举一反三:如下代码
Fangio同学常用二分模板
bool judge(int a,int x){
return x>a;
}
bool Judge(int a,int x){
return a==x;
}
//要返回x在数组中的位置
//(如果没有的话)就返回第一个比x小的数的位置
int Bin_search(vector<int> &arr,int x,int n){
//默认arr数组是排序好了升序排列
//而且下标为0的位置不存储目标元素,也就是说l和r的起始位置都不在目标数组内部)
int l=0,r=n,mid;
while(l+1<r){
mid=(l+r)>>1;
if(Judge(arr[mid],x)){
return mid;
}
else if(judge(arr[mid],x)){
l=mid;
}else r=mid;
}
//如果要返回第一个比x小的位置的话就返回l
//如果返回第一个比x大的数的位置的话就返回r
return r;
}
个人觉得好理解且好用,把所有比target小的元素是红色,大的是灰色,然后每次mid不符合要求的时候要么把他归入到红色区,要么归入灰色区,如此下来,最后把所有元素涂上颜色,那么l和r就分别指向了红色区最右侧以及最左侧,返回第一个比x小的位置的话就返回l,反之,如果返回第一个比x大的数的位置的话就返回r
好处如下:
- 对于不同的题判断条件不变,只需要改变Judge函数
- 对于不同的题,while后面括号里的东西不变,保持l+1<r
- 对于不同的题,不需要考虑mid用不用+1,只管l=mid或者r=mid
- 只改变return的值就可以改变返回较小还是较大
拓展模板
我想返回x在数组中的位置或者是第一个比x小的位置
见过的其他二分应用
int l = 1, r = len, pos = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (d[mid] < nums[i])
{//此处nums[i]是target
pos = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
d[pos + 1] = nums[i];
}
作者:力扣官方题解
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
此题链接,只不过这道题还考察了dp,那部分我就不说了
然后,如果遇到了还有其他的我再去发