一、为什么不能用upper_bound
和lower_bound
函数?
当题目是一道以二分搜索算法为核心的题目时,这种时候一般会设置数据把这两个函数卡掉,所以我们经量用手打的二分搜索。而一般我们用二分搜索来优化一个算法的时候,比如线段树需要离散一下点,排序后需要二分点找离散值,这种时候就可以使用二分搜索函数。
二、upper_bound
和lower_bound
的用途与区别
lower_bound的第三个参数传入一个元素x,在两个迭代器(或指针)指定的部分上执行二分查找,返回指向第一个大于等于x的元素
的位置的迭代器(或指针)。
upper_bound的用法和lower_bound大致相同,唯一的区别是查找第一个大于x的元素
。当然,两个迭代器(或指针)指定的部分应该是提前排好序的。
需要注意的是如果原来的序列中就有一个或多个x,那么lower
是找到大于等于第一个x的位置。而upper
是插入最后一个x后面的位置。
- 在有序int数组(元素存放在下标1-n)中查找大于等于x的最小整数的下标:
int i=lower_bound(a+1,a+n+1) - a;
- 如果是查找具体的数值:
int ans = *lower_bound(a+1,a+1+n,N);
二分的时间复杂度为\(O(logn)\),\(n\)为序列长度。
需要注意的是,\(mid\)的取值不要写成\((l + r) / 2\),因为如果\(l + r\)很大的话,会溢出。因此写成\(l +(r - l)/2\)就不会有这个问题。
更进一步,当然也可以用位运算来\(l +((r - l)>>1)\)代替(这里要注意是先除以2,再加上l!!+的优先级要比>>高,所以要加括号),而且这种位运算的效率更高一些。
三、手写lower_bound与STL lower_bound的对比测试
#include <bits/stdc++.h>
using namespace std;
const int N = 7;
int a[N] = {1, 3, 3, 3, 5, 7, 9};
/**
* 功能:二分lower_bound模板
* @param l
* @param r
* @param k
* @return
*/
int lower_bound(int l, int r, int k) {
while (l <= r) {
int mid = l + ((r - l) >> 1);
if (k > a[mid]) l = mid + 1;
else r = mid - 1;
}
return l > n+1 ? n+1 : l;
}
//测试用例:
/**
k=-1 左侧出界
k=2 区间内不存在
k=3 区间内存在,多个
k=5 区间内存在,1个
k=15 右侧出界
*/
int kArray[] = {-1, 2, 3, 5, 15};
string kMessage[] = {"左侧出界", "区间内不存在", "区间内存在,并且多个", "区间内存在,1个", "右侧出界"};
int main() {
//一定要升序
sort(a, a + N);
for (int i = 0; i < 5; i++) {
int k = kArray[i];
cout << "k=" << k << " " << kMessage[i] << endl;
//STL--> lower_bound 大于等于k的第一个位置
int p = lower_bound(a, a + N, k) - a;
cout << p << endl;
//模板1:与lower_bound完全匹配
int l = 0, r = N;
cout << lower_bound(l,r,k)<< endl;
cout << "*************************************************\n";
}
//结论:两者完全一致,没有区别
return 0;
}
四、手写upper_bound与STL upper_bound的对比测试
#include <bits/stdc++.h>
using namespace std;
const int N = 7;
int a[N] = {1, 3, 3, 3, 5, 7, 9};
/**
* 功能:二分upper_bound模板
* @param l
* @param r
* @param k
* @return
*/
int upper_bound(int l, int r, int k) {
while (l <= r) {
int mid = l + ((r - l) >> 1);
if (k >= a[mid]) l = mid + 1;
else r = mid - 1;
}
return l > n+1 ? n+1 : l;
}
//测试用例:
/**
k=-1 左侧出界
k=2 区间内不存在
k=3 区间内存在,多个
k=5 区间内存在,1个
k=15 右侧出界
*/
int kArray[] = {-1, 2, 3, 5, 15};
string kMessage[] = {"左侧出界", "区间内不存在", "区间内存在,并且多个", "区间内存在,1个", "右侧出界"};
int main() {
//一定要升序
sort(a, a + N);
for (int i = 0; i < N; i++) cout << a[i] << " ";
cout << endl << endl;
for (int i = 0; i < 5; i++) {
int k = kArray[i];
cout << "k=" << k << " " << kMessage[i] << endl;
//STL--> upper_bound 大于k的第一个位置
int p = upper_bound(a, a + N, k) - a;
cout << p << endl;
//模板2:与upper_bound完全匹配
int l = 0, r = N;
cout << upper_bound(l, r, k) << endl;
cout << "*************************************************\n";
}
//结论:两者完全一致,没有区别
return 0;
}
为了防止出错,可以只记住有重复数据时的写法,在无重复数据时也通用!!!