深入二分思想
二分在实际使用中常常会出现死循环的问题
这是因为我们对二分临界状态的不熟悉而导致的,这里介绍一种通用的分界想法:
整数二分:
int l = begin - 1, r = end + 1;
while (l + 1 != r) {
int mid = l + r >> 1;
if (judge(mid)) l = mid;
else r = mid;
}
规定当 l + 1 == r
时,退出二分查找,因为在对整数区间进行二分时,最终状态的 l
和 r
是紧密相邻的
judge
函数同样需要进行设计:
1 2 3 4 4 4 5 6
针对以上序列,我们如果想查找:(下标从1开始)
<4 <=4 >4 >=4
的元素下标,那么我们二分的 judge
和 返回的值就不同:
//查找 <4 <=4 时:
bool judge(int x){
if (arr[x] < 4) return 1;
return 0;
}
//最终状态的边界线处在3和4之间(下标)
//二分最后的l就是3,r就是4(下标)
这样的judge
函数,我们可以查找出 <
和<=
的位置
同样的:
//查找 >4 >=4 时:
bool judge(int x){
if (arr[x] <= 4) return 1;
return 0;
}
//最终状态的边界线处在6和7之间(下标)
//二分最后的l就是6,r就是7(下标)
这样的judge
函数,我们可以查找出 >
和>=
的位置
在上述的judge
函数中,我们不难写出l
和r
的位置关系,但是为什么只用在judge
中,添加一个=
就能改变查找的方向呢?
还是以这段序列为例:
1 2 3 4 4 4 5 6
查找 <
和<=
时,我们目标是把边界线定位在3
和4
之间
if (arr[x] < 4) return 1;//(l=mid)
else return 0;//(r=mid)
因此,当我们的arr[mid]==4
时,我们就要将右边界移动到mid
反之:
查找 >
和>=
时,我们目标是把边界线定位在4
和5
之间
if (arr[x] < 4) return 1;//(l=mid)
else return 0;//(r=mid)
因此,当我们的arr[mid]==4
时,我们就要将左边界移动到mid
也可以使用stl
库内的 upper_bound()
和lower_bound()
函数进行二分查找
需要提前对查找的序列进行有序化处理
用法如下:x
为需要查找的元素
//对于静态数组
upper_bound(arr+begin,arr+len,x)
lower_bound(arr+begin,arr+len,x)
//对于stl容器
upper_bound(arr.begin(),arr.end(),x)
lower_bound(arr.begin(),arr.end(),x)
其中,在对静态数组进行查找时,arr+begin
为从第几个元素开始查找,len
为需要查找的区间长度,arr+len
则表示查找到第几个元素停止
upper_bound()
返回的是第一个 大于 x
的迭代器,lower_bound()
返回的是第一个 大于等于 x
的迭代器
对于同样的序列:arr[]={1 2 3 4 4 4 5 6 }
upper_bound(arr, arr+8, 4);
lower_bound(arr, arr+8, 4);
//通过*解引用处理
cout << *upper_bound(arr, arr + 8, 4) <<' ' << *lower_bound(arr, arr + 8, 4);
//输出的结果为 5 4
注意upper_bound()
和lower_bound()
返回的是迭代器,可以通过 -
操作计算他们的绝对距离(大于0),就是等于4
的元素的个数