二分搜索可以在有序序列中,以logN的时间复杂度快速找到答案。常见的问题有三种,第一:找到目标值,第二:找到符合要求的最小值(搜索左边界),第三:找到符合要求的最大值(搜索右边界)。对应的二分搜索也有三种形式。
注意:二分搜索也可以是逻辑上的
第一种:标准二分搜索,返回目标值下标,没找到返回-1
int binarySearch(vector<int>&arr,int target){
int left=0,right=arr.size()-1;
while(left<=right){
int mid=left+(right-left)/2;
if(arr[mid]==target)
return mid;
else if(arr[mid]<target)
left=mid+1;
else
right=mid-1;
}
return -1;
}
第二种:搜索左边界,返回目标值下标,没找到目标值,返回-1
//找到大于等于target的最小值,返回下标
int binarySearchLeft(vector<int>&arr,int target){
int left=0,right=arr.size()-1;
while(left<=right){
int mid=left+(right-left)/2;
if(arr[mid]>=target)
right=mid+1;
else
left=mid+1;
}
return left<arr.size() && arr[left]==target?left:-1;
}
第三种:搜索右边界,返回目标值下标,没找到返回-1
int binarySearchRight(vector<int>&arr,int target){
int left=0,right=arr.size()-1;
while(left<=right){
int mid=left+(right-left)/2;
if(arr[mid]<=target){
left=mid+1; //收缩左边界
}
else
right=mid-1; //收缩右边界
}
return right!=-1 && arr[right]==target?right:-1;
}
这样写更优
/**
* 有序数组中查找小于等于key的最大值的下标,如果没有则返回-1
* @param arr
* @param key
* @return
*/
int findRight(vector<int>&arr,int key){
int l=0,r=arr.size()-1;
int ans=-1;
while(l<=r){
int mid=l+(r-l)/2;
if(arr[mid]<=key){
ans=mid;
l=mid+1;
}else{
r=mid-1;
}
}
return ans;
}
我们每次找到找到符合条件的数字时,就将其赋值给ans。直到搜索空间[l,r]缩减至空。
解释一下这里为什么l=mid+1
而不是l=mid
。我们的二分过程相当于在不断优化ans的值,当mid符合条件时,赋值给ans以优化其结果,然后需要将搜索空间移到mid右侧来搜寻是否有更优的结果。
注意:为什么搜索空间是闭区间[l,r]
,是因为l=mid+1
和r=mid-1
,使得左边界和有边界一定会被mid访问到。