一、为什么不能用upper_boundlower_bound函数?

当题目是一道以二分搜索算法为核心的题目时,这种时候一般会设置数据把这两个函数卡掉,所以我们经量用手打的二分搜索。而一般我们用二分搜索来优化一个算法的时候,比如线段树需要离散一下点,排序后需要二分点找离散值,这种时候就可以使用二分搜索函数。

二、upper_boundlower_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;
}
为了防止出错,可以只记住有重复数据时的写法,在无重复数据时也通用!!!