二分查找为什么总写错?

二分查找:是在某一特定有序数组中查找指定数字的一种算法
如下查找数字:5的步骤
{0, 1, 2, 3, 4, 5, 6, 7, 8}

  1. 第一步折半查找结果:中间下标是4,结果是4
  2. 第二次折半查找结果:中间下标是6,结果是6
  3. 第三次折半查找结果:中间下标是5,结果是5

相信这是都知道的一个步骤和思路,但是小细节问题就出现在边界条件和更新左右指针

1.举例

1 2 3 5 5 5 8 9

  1. 找到第一个">=5"的元素【三个5中第一个5】
  2. 找到第一个"<5"的元素【三个5中第一个5的前一个元素】
  3. 找到第一个">5"的元素【三个5中最后一个5的后一个元素】
  4. 找到第一个"<=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**

  1. 当我们 l 初始化为0,r 初始化为 N-1的时候,假设整个数组都是蓝色【也就是整个数组都小于 K,则 r
  2. 出现在一个全部蓝色区间内是一个错误;反知 l 的出现就是错误】

2.细节2

mid是否始终处于[0,N) 以内【不会数组越界访问】**

  1. 我们发现只有 m 处于[0,N)或者[0, N-1]的区间内才是有意义的
  2. mid 的最小值 = (l 的最小值 + r 的最小值)/2
  3. l 的最小值就是 -1;r能够进入 while 循环体则 r 的最小值就是 1【为什么是 1 而不是 0?:因为当r=0的时候则while循环进不去】
  4. 由此可得出 mid 的最小值就是(-1+1)/2=0
  1. mid 的最大值 = (l 的最大值 + r 的最大值)/2
  2. r 的最大值就是它的初始值 N;r能够进入 while 循环体,则 r 的最大值就是 N-2【如果是N-1则 l+1 != r不成立,不会进入循环体】
  3. 都此可得 mid 的最大值就是(N-2+N)/2 = N-1

3.细节3

更细指针时,能不能写成 l=mid+1,或者r=mid-1?**
在这里插入图片描述

l 只能指向蓝色区间,r只能指向黄色区间。不可以越界指向其它区间,因此这种二分查找模版不能用其它二分查找的加减一操作来移动mid

4.细节4

会不会陷入死循环?**
在这里插入图片描述

其余情况会发现无论如何都会回归到第一种情况

5.举例问题的答案

在这里插入图片描述

6.总结一般流程

  1. 建模:划分蓝黄区域,确定IsBlue()
  2. 确定返回 l 还是 r
  3. 套用算法模版
  4. 后处理…根据实际情况

おすすめ

転載: blog.csdn.net/weixin_45364220/article/details/120401856