二分查找:是在某一特定有序数组中查找指定数字的一种算法
如下查找数字:5的步骤
{0, 1, 2, 3, 4, 5, 6, 7, 8}
- 第一步折半查找结果:中间下标是4,结果是4
- 第二次折半查找结果:中间下标是6,结果是6
- 第三次折半查找结果:中间下标是5,结果是5
相信这是都知道的一个步骤和思路,但是小细节问题就出现在边界条件和更新左右指针
1.举例
1 2 3 5 5 5 8 9
- 找到第一个">=5"的元素【三个5中第一个5】
- 找到第一个"<5"的元素【三个5中第一个5的前一个元素】
- 找到第一个">5"的元素【三个5中最后一个5的后一个元素】
- 找到第一个"<=5"的元素【三个5中最后一个5】
新的角度
继续用开篇中{0, 1, 2, 3, 4, 5, 6, 7, 8}的例子
开始的时候数组颜色为灰色
2.朴素算法
蓝黄指针只需要一个即可。
假设是蓝色指针,最开始指向-1位置,让它从0移动,直到找到我们需要的蓝黄边界K就停止【黄色指针,指向N位置,让它从N-1开始移动,直到找到我们的蓝黄边界K就停止】
这种朴素算法的时间复杂度是O(N)
3.二分查找
第一次循环:我们找到的是元素4
l=-1;
r=N;
mid = (l+r)>>1;
l=-1;
r=9;
mid=4;
找到的4并不是我们想要的5且比4小,因此4左边界全部为蓝色
第二次循环:我们找到的元素是6
l=4;
r=9;
mid=6
找到的4并不是我们想要的6且比5大,因此6右边界全部为黄色
第三次循环:我们找到的元素是5
l=4;
r=6;
mid=5;
通用代码模版
l=-1, r=N;
while (l+1!=r){
mid=(l+r)>>1;
if(isBlue){
l=mid;
}else{
r=mid;
}
}
return l or r;
初始:l指向蓝色区域,r指向黄色区域
循环:l, r快速向蓝黄边界靠近。保持l, r颜色不变
结束:l, r刚好指向蓝黄边界
二分查找的时间复杂度为O(logN)【1+2+3+…+N-1+N: 等差公式计算可得(N(1+N))/2–>时间复杂度就是N^2由于每次都是折半查找所以,查找多少次才能得到N^2呢?答案是就是logN】
4.细节总结
1.细节1
为什么 l 的初始值为-1,r初始值为N**
- 当我们 l 初始化为0,r 初始化为 N-1的时候,假设整个数组都是蓝色【也就是整个数组都小于 K,则 r
- 出现在一个全部蓝色区间内是一个错误;反知 l 的出现就是错误】
2.细节2
mid是否始终处于[0,N) 以内【不会数组越界访问】**
- 我们发现只有 m 处于[0,N)或者[0, N-1]的区间内才是有意义的
- mid 的最小值 = (l 的最小值 + r 的最小值)/2
- l 的最小值就是 -1;r能够进入 while 循环体则 r 的最小值就是 1【为什么是 1 而不是 0?:因为当r=0的时候则while循环进不去】
- 由此可得出 mid 的最小值就是(-1+1)/2=0
- mid 的最大值 = (l 的最大值 + r 的最大值)/2
- r 的最大值就是它的初始值 N;r能够进入 while 循环体,则 r 的最大值就是 N-2【如果是N-1则 l+1 != r不成立,不会进入循环体】
- 都此可得 mid 的最大值就是(N-2+N)/2 = N-1
3.细节3
更细指针时,能不能写成 l=mid+1,或者r=mid-1?**
l 只能指向蓝色区间,r只能指向黄色区间。不可以越界指向其它区间,因此这种二分查找模版不能用其它二分查找的加减一操作来移动mid
4.细节4
会不会陷入死循环?**
其余情况会发现无论如何都会回归到第一种情况
5.举例问题的答案
6.总结一般流程
- 建模:划分蓝黄区域,确定IsBlue()
- 确定返回 l 还是 r
- 套用算法模版
- 后处理…根据实际情况