首先推荐一位业内大神 labuladong 看完大神的二分查找详解才算把二分搜索弄明白
二分搜索简单中的不简单,为什么这么说呢? 根据自身体会,细节处理不好过题全靠运气!
先来看一首小诗吧,帮助记忆。
二级搜索升天词 作者:labuladong 二分搜索不好记,左右边界让人迷 小于等于变小于, mid加一又减一 就算这样还没完,return应否再减一 信息慢慢刷力扣,AC比率二十一 我本将心向明月,奈何明月照沟渠 问君能有几多愁,恰似深情喂了狗 labuladong从天降,一同手撕算法题 赠军一法写二分,不用拜佛与念经 管他左侧还右侧,搜索区间定乾坤 搜索一个元素时,搜索区间两端闭 while条件带等号,否则需要打补丁 if相等就返回,其他的事甭担心 mid必须加减一,因为区间两端闭 while结束就凉了,凄凄惨惨返-1 搜索左右边界是,搜索区间要阐明 左闭右开最常见,其余逻辑便自明 while要用小于号,这样才能不漏掉 if相等别返回,利用mid锁边界 mid加一或减一?要看区间开或闭 while结束不算完,因为你还没返回 索引可能出边界,if检查保平安 左闭右开最常见,难道常见就合理? labuladong不信邪,偏要改成两端闭 搜索区间记于心,或开或闭有何异 二分搜索三变体,逻辑统一容易记 一套框架改两行,胜过千言和万语 此等神人何处寻?全靠缘分不可期 labuladong公众号,开启算法新天地
一、搜索一个数(最基本二分搜索)
int binarySearch(int nums[],int target){
int left = 0, right = nums.length()-1;
int mid = left+(right-left)/2; // 与mid = (left + right)/2 效果相同,但是这样有效的避免了数组的越界问题;
while(left <= right){
if(nums[mid] == target) return mid;
else if(nums[mid] > target) right = mid -1;
else if (nums[mid] < target) left = mid + 1;
}
return -1;
}
1、为什么是left <= right ?
举个栗子:在区间[2,2]中,left == right 则会跳出循环并不寻找nums[2]的值
2、为什么left = mid + 1,right = mid-1?
我们在[left,right]区间当中寻找target在left,right区间当中寻找的mid并没有对应target那应该在哪里寻找呢当然是在[left,mid-1]或者在[mid+1,right]当中。因为mid刚刚被搜索过因此不包括。
搜索左边界
int binary_left(int nums[],int target){
int left = 0,right = nums.length();
while(left < right){
int mid = left + (right - left) / 2;
if(nums[mid] == target) right = mid;
else if (nums[mid] > target) left = mid+1;
else if (nums[mid] < target) right = mid;
}
return left;
}
1、为什么这次的条件改为了left < right
其实left <= right也完全可以,但是必须明确【搜索区间】的概念。这个条件下,while 终止的条件为 left == right 也就是搜索的区间为[left,right)区间,因为此时right的初始的设置为nums.length()而并非为nums.length()-1。
2、为什么right = mid而不是mid-1
根据上一个问题我们知道,查询到区间为[left,right)为左闭右开区间,我们并不对right进行查询,也就是mid已经查询过,在下一个区间当中,我们并不对mid进行查询,因此我们的right为mid。
3、为什么nums[mid] = target 时,right = mid
我们寻找的是左边界,因此我们对右边界是压缩的直到left = right
如果运用和搜索一个数相同的搜索区间如何写:
int binary_left(int nums[],int target){
int left = 0, right = nums.length()-1;
if(nums.length() == 0) return -1;
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid] == target) right = mid -1;
else if (nums[mid] > target) right = mid -1;
else if (nums[mid] < target) left = mid + 1;
}
/*检查出界的情况*/
if(left > nums.length() || nums[left] != target) return -1;
return left;
}
三、搜索右边界
与搜索左边界是相同的道理,这里不多做赘述,直接上代码。
/*搜索的区间为左闭右开*/
int binary_right(int nums[],int target){
int left = 0, right = nums.length();
while(left < right){
int mid = left + (right - left) / 2;
if(nums[mid] == target) left = mid + 1;
else if (nums[mid] > target) left = mid -1;
else if (nums[mid] < target) right = mid;
}
return left - 1;
}
1、为什么返回值为left-1
我们可以在脑中进行一次循环,当进行到最后一次循环时。这时的left = mid + 1 (可以看做mid = left -1) , nums[left] 的值肯定不等于target,mid是我们所需要的最后结果因此返回值为left-1
最后可以自己练习力扣的三道习题
278. 第一个错误的版本
35. 搜索插入位置
二分查找