关于二分搜索 简单、左侧区间、右侧区间
作者:互联网
4月15号阅文一面,问完八股之后被问到了二分搜索,我啪的一下就写出来了,很快啊!面试官也很高兴,想加大一点难度,让我写一下搜索左侧区间的二分搜索。
什么是搜索左侧区间的二分搜索呢?比如一个排序数组里有某个元素重复出现了多次,我们的二分搜索必须每次返回这个元素第一次出现的位置。比如数组 0, 1, 2, 4, 4, 4, 6, 7, 9
输入target = 4
时我们的函数应该返回3
。
然而啊,我大意了,对这道题的理解还不够深刻,面试的时候写的不对。虽然被面试官指出了问题也改正了,但是面试凉凉了。大家要学好二分搜索啊!不能像我一样。
从最基础的二分搜索谈起
int binSearch(vector<int> &nums, int target){
int lo = 0, hi = nums.size() - 1; // 将hi设为这个值,意味着搜索期间为闭区间[lo, hi]
while(lo < hi){ // 因为区间是闭区间,跳出循环的条件是lo < hi 而不是 <=
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target){
return mid;
}
else if(nums[mid] < target){ // 中点值比目标值小,说明目标在右侧区间
lo = mid + 1;
}
else if(nums[mid] > target){ // 中点值比目标值大,说明目标在左侧区间
hi = mid - 1;
}
// 小细节:多写几个else if ,而不是直接一个else 这样可以方便对代码的查看和思路的疏通。
}
return -1; // 没有找到,返回-1
}
几个比较重要的点已经在注释中标注了,最简单的二分查找在遇到有重复数字的数组时,可能会返回其中符合条件的随机一个位置。因此,如果我们要返回区间左侧或者右侧坐标,需要对其稍稍改变。
返回区间左侧或右侧坐标的二分查找
int findLeft(vector<int>& nums, int target){
int lo = 0, hi = nums.size() - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target)
hi = mid - 1; //注意
else if(nums[mid] > target)
hi = mid - 1;
else if(nums[mid] < target)
lo = mid + 1;
}
if(lo >= nums.size() || nums[lo] != target) return -1; //lo可能在合法区间之外(当任意nums[i] < target 均成立时),也可能nums数组中不存在target。
return lo;
}
注意,当我们的nums[mid] == target
已经成立时,不要直接返回,而是继续收缩区间右侧,令其缩减到目标值出现位置的左侧。我们跳出循环的条件一定是lo == hi - 1
, 也就是说,跳出循环时,lo
的位置即指向nums[mid] == target
最后一次成立的位置。
搜索右侧区间的算法同理,此处供参考。
int findRight(vector<int>& nums, int target){
int lo = 0, hi = nums.size() - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target)
lo = mid + 1;
else if(nums[mid] > target)
hi = mid - 1;
else if(nums[mid] < target)
lo = mid + 1;
}
if(hi < 0 || nums[hi] != target) return -1;
return hi;
}
推荐阅读:labuladong:我写了首诗,让你闭着眼睛也能写对二分搜索
推荐练习: Leetcode 34
标签:二分,target,nums,int,lo,mid,hi,区间,右侧 来源: https://www.cnblogs.com/wstnl/p/16152026.html