二分
二分主要解决的是,满足某一条件的最值问题,二分能解决的问题的性质具有二端性,即最值界点的左侧都满足或都不满足该性质,相对的右侧都不满足或都满足该性质。特殊的需要判断如果有重复值出现时最早和最晚的判断,相等是是收缩上界还是增大下界,即有以下两种情况:
1.最早值(
若
相
等
收
缩
上
界
若相等收缩上界
若相等收缩上界)
2.最晚值(
若
相
等
则
增
大
下
界
若相等则增大下界
若相等则增大下界)
以在一个升序数组中查找一个数为例。
它每次考察数组当前部分的中间元素,如果中间元素刚好是要找的,就结束搜索过程;如果中间元素小于所查找的值,那么左侧的只会更小,不会有所查找的元素,只需到右侧查找;如果中间元素大于所查找的值同理,只需到左侧查找。
时间复杂度:O(logN)
空间复杂度:O(1)
题目
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的最早出现的位置和最晚出现的位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1。
给定数组:1 2 2 2 3 4
寻找:2
输出: 2 ,4
最小值条件:a[左]<2 a[临界点]=2 a[右]>=2
最大值条件 :a[左]<=2 a[临界点]=2 a[右]>2
可以发现:最大值与最小值的条件是不同的,对应不同的条件二分有不同的模板。
最小值模板
int l=0,r=n-1;
while(l<r)
{
int mid=l+r>>1;
if(num[mid]>=t)r=mid;
else l=mid+1;
}
return l
最大值模板
int check(int t)
int l=0,r=n-1;
while(l<r)
{
int mid=l+r+1>>1;
if(num[mid]<=t)l=mid;
else r=mid-1;
}
return l;
区别
这两种模板需要注意的有两个点:
1.a[i]==t时是应该将上界先左还是下界向右
2.mid=(l+r)>>1 还是 (l+r+1)>>1
当r=mid-1时需要用后者,理由是当r=mid-1=l时而又满足num[mid]<=t时会死循环。
二分答案
解题的时候往往会考虑枚举答案然后检验枚举的值是否正确。若满足单调性,则满足使用二分法的条件。把这里的枚举换成二分,就变成了“二分答案”。此举仅仅为优化枚举,主要是判断某个值是否满足题目给定的性质。
三分
三分法可以用来查找凸函数的最大(小)值。
如果 lmid 和 rmid 在最大(小)值的同一侧:由于单调性,一定是二者中较大(小)的那个离最值近一些,较远的那个点对应的区间不可能包含最值,所以可以舍弃。
如果在两侧:由于最值在二者中间,我们舍弃两侧的一个区间后,也不会影响最值,所以可以舍弃。
int SanFen(int l,int r) //找凸点
{
while(l < r-1)
{
int mid = (l+r)/2;
int mmid = (mid+r)/2;
if( f(mid) > f(mmid) )
r = mmid;
else
l = mid;
}
return f(l) > f(r) ? l : r;
}