二分总结
虽然二分是一个很基础的算法,可是其重要性是十分显然的
二分的思想也可以扩展到许多更高级的算法和数据结构上
所以有必要学好二分的本质和精髓,掌握二分的许多细节
我们先来看一个最最基本的例子:
给一个排好序的长为n的序列
求出大于等于k的第一个位置
很显然,我们并不需要一个一个查找,我们假设要求的位置在区间[L,R]中
取中点mid,若中点处的值大于或等于k,那么答案一定[L,mid]这个区间内
注意到mid是有可能成为答案的,所以划分区间时要注意
但如果mid处的值小于k,那么mid一定不会成为答案,所以答案区间在[mid+1,R]中
在划分区间时,我们发现有[L,mid]这个区间,所以mid的值不能与R相等,不然可能死循环
所以我们去mid时要向下取整,避免这种现象
while(r>l) { int mid=l+r>>1; if(a[mid]>=k) r=mid; else l=mid+1; }
将问题转换一下
给一个排好序的长为n的序列
求出小于等于k的第一个位置
这与上面的问题其实也是类似的
将区间划分为[L,mid],[mid+1,R]两个部分
只不过当a[mid]小于等于k时,可能的答案区间为[mid,R]
而当a[mid]大于k时可能的答案区间为[L,mid-1]
这样的话为了避免死循环,我们就得将mid向上取整
即mid=(l+r+1)/2;
while(r>l) { int mid=(l+r+1)>>1; if(a[mid]<=k) l=mid; else r=mid-1; }
我们来深入研究二分的本质,发现二分降低复杂度的根本原因其实是因为答案的单调性
那么,其实所有具有单调性的答案其实都是可以通过二分来查找的
我们也称答案具有二分性
对于具有二分性的答案,往往在有求满足条件最小或最大的值时,就可以利用二分来求解了