总结:二分查找的目标有两个,一个是左区件的右边界,一个是右区间的左边界
如何去理解二分的过程?
如果要查找的是左区间的右边界:
可以将[l, r]
理解一个集合,这个集合范围内的数都有可能是最后需要得到的目标值
--左区间的右边界这个点
每次要做的事情就是去缩小(更新)这个集合,最终使得集合里只有一个元素,那个元素就是目标值
每一次用mid
去找集合的中点,有一个大前提:结合的结构[需要的值的集合|不需要的值的集合]
为了方便理解这个集合,我们可以理解为假设你有一个飞镖(也就是
mid
),有一个靶子(就是这个数组),靶子有内环(边界点所在的区间或者说集合)和外环(我们要找的集合的补集),靶子有一个中心点(内环的边界,也就是需要找到左区间的右边界)。
mid
的位置有两种情况:落在左区间 里或者落在右区间里
- 如果落在了左区间,相当与告诉你了一个信息:从这个地方包括这个地方(
mid
所在的索引)往后([mid, r]
),可能会发现右边界。与之对应的信息是右边界绝对不在mid
左边的集合内([l, mid-1]
),所以左边的集合([l, mid-1]
)就可以被删掉了,要实现这个操作只需要让下一次判断的集合是[mid, r]
就i可以了,等价于l=mid
- 如果落在了右区间,相当于告诉了你:这个点往右(包括这个点)都不是我要的区间,需要被删掉。那就直接令
l = mid-1
,删除集合中绝对不是答案的那一坨,也就是右边那一坨 - 这样不断的往复删除确定的无用集合,最后就可以得到一个目标集合。
对于查找到是右区间的左边界也是一个原理。
mid
的计算是左偏还是右偏怎么去判断?
关于mid
的计算方式mid=i+j >> 1
还是(mid=i+j>>1) + 1
,个人有一种比较好理解的方法:
同样分两种情况:
- 找左区间右边界
- 找右区间左边界
左区间右边界
- 首先要明白产生边界问题的原因在哪里
奇数个集合的时候,对于两者mid
都是指向中间元素,但是如果集合个数是偶数,他们的中点是谁?很显然,两者都不是中点(1 中点是我 2
中的那是1
和2
中间的文字吧)。
所以要么去文字左边的1
为中点,要么取文字右边的2
为中点。这样一来这个中点其实就不是真正意义上的中点了。
首先说结论,取右边界需要右偏,我们来分析一下:
对于集合很大的时候,是没有问题的,边界问题只会发生在集合很小的时候,所以只有拿出一个很小的集合,一个快被处理结束的集合,才能够知道为啥左偏会进入死循环,为啥不能用它这个问题。
所以直接去分析两个元素时有哪些情况:(O表示的是左区间右边界所在的集合,X表示待删除的集合)
- [O, O]
- [X, X]
- [O, X]
- [X, O]
其中,[X, O]这种情况不可能存在,因为左区间的相对顺序一定在左边
左边界l
是指向第一个元素的,右边界r
是指向第二个元素的
mid
如果找到目标元素并不会剔除,并不会缩小范围,所以这个时候想要缩小范围就需要mid
起作用了
对于[O, O]二分查找如果左偏会去看第一个元素,包含它,这没什么问题,只要我们的mid
下一次去往右看就行了,但是问题就在于下一次mid
看左边还是看右边是和左偏绑定在一起的,所以如果左偏,下一次mid
还会看左边,如果右偏下一次mid
还会看右边,但是我们需要看右边啊!所以只能够右偏了,这样遇到这种情况mid
也能继续向右看去。
同样的对于右区间左边界的判断也是如此
- [O, O]
其实说白了就是,两个元素时,找右边界时遇到需要的元素l
会直接落在mid
上进行更新。
找左边界时, 遇到需要的元素,r
会直接落在mid
上进行更新,为了防止更新后继续重合。
-
找右边界,
mid
下一次查找只能偏右更新,避开l
防止死掉 -
找左边界,
mid
下一次查找就只能偏左更新,避开r
防止死掉