一、二分查找的二种写法
1.[left,right]左闭右闭
2.[left,right) 左闭右开
二、二分查找的二类需求
1.在最左边找(升序情况下的第一个target)
2.在最右边找(升序情况下的最后一个target)
看到根据区间来写二分,注意力好的朋友应该可以想到数组,我们通常处理[0,len-1]惑者[0,len)。是滴,就是这样的。
1.左闭右闭
int binary_search(int arr[],int len,int x){
int l=0;
int r=len-1;
while (l<=r){//左闭右闭情况l=r有意义!!
int mid=l+(r-l)/2;//防止l+r数值溢出
if (arr[mid]==x){
return mid;//找到了
}else if (arr[mid]>x){
r=mid-1;//左区间,更新右边界
}else l=mid+1;//右区间,更新左边界
}
return -1;
}
举个例子来帮助理解,假设目标值在左区间,更新右边界r=mid-1,即[left,right]->[left,mid-1],不漏值;
2.左闭右开
int binarysearch(int arr[],int len,int x){
int l=0;
int r=len;
while (l<r){
int mid=l+(r-l)/2;
if (arr[mid]==x){
return mid;//找到了
}else if (arr[mid]>x){
r=mid;//左区间,更新右边界
}else l=mid+1;//右区间,更新左边界
}
return -1;
}
左闭右开情况下l=r无意义,所以循环条件为l<r。很简单,举个例子来帮助理解,假设目标值在左区间,更新右边界r=mid;为什么不-1呢?很简单,如果-1,那么[left,right]->[left,mid-1),若mid-1恰好是target呢?\滑稽
二分查找我们已经会了,但如果给定1,3,7,7,7,7,9,83这个序列,求7第一次出现的位置。哦豁,直接一手二分好像不得行咯~
我们来复习一下二分的流程
假设我们要在有序数组中查找目标元素,数组中的元素可以是重复的。当我们比较中间元素与目标元素大小时,有三种情况可能发生:
-
中间元素等于目标元素:找到了目标元素,返回中间元素的索引值。
-
中间元素大于目标元素:目标元素在中间元素的左边,继续在左半部分进行二分查找。
-
中间元素小于目标元素:目标元素在中间元素的右边,继续在右半部分进行二分查找。
显然我们发现问题出在步骤1,找到目标元素后不要急,往左/右再找看看,直到出现第一个不同元素。我们可以通过以下两种方式来处理:
-
查找第一个与目标元素相等的元素:在比较中间元素与目标元素大小时,如果相等,继续在左半部分进行查找,直到找到第一个与目标元素不相等的元素。这样可以得到目标元素在数组中的最左边的索引值。
-
查找最后一个与目标元素相等的元素:在比较中间元素与目标元素大小时,如果相等,继续在右半部分进行查找,直到找到第一个与目标元素不相等的元素。这样可以得到目标元素在数组中的最右边的索引值。
用左闭右闭区间写法来解决这个问题
int binary_search(int arr[],int len,int x){
int l=0;
int r=len-1;
int result=-1;
while (l<=r){
int mid=l+(r-l)/2;
if (arr[mid]==x){
result=mid;//更新结果
r=mid-1;//继续在左半部分查找
}else if (arr[mid]>x){
r=mid-1;
}else l=mid+1;
}
return result;
}
在这个代码中,我们使用了变量result
来保存第一个与目标元素相等的索引值。当遇到与目标元素相等的元素时,我们将result
更新为当前的中间元素的索引,然后继续在左半部分进行查找,直到找到最左边的索引值。如果数组中不存在目标元素,result
的初始值-1将会被保留。
在最右边找同理,就不做介绍了。
有误的地方请大家评论区交流
更新一下对二分新的理解:
前面我们已经论述了如何实现查找第一个出现的target或最后一个出现的target。
可以发现这二个if分支是可以合并的,那么我们就得到了更优的代码,发现已经有大佬有很好的总结,如图(图片来自该大佬博客)
参考:二分查找 & 二分答案 万字详解,超多例题,带你学透二分。-CSDN博客
可以肯定的是这种方法肯定适用,引用文章使用的左闭右开写法,抽象程度有点高,大家可以看我这篇文章从0到精通,然后去这位大佬博客下刷刷例题。
还是写一个左闭右开的模板,代码示例如下
int binary_search(int arr[], int len, int x) {
int l = 0, r = len;
while (l < r) {
int mid = l + (r - l) / 2;
if (arr[mid] >= x) r = mid;
else l = mid + 1;
}
return l;
}
这个check()看了文章的应该都懂。