C++中的 lower_bound 和 upper_bound:一篇讲清楚

在刷题或者写代码的时候,lower_bound 和 upper_bound 是两个非常高频但又容易用错的函数。很多人知道"能用",但不知道"为什么这样用"。

这篇文章不搞花里胡哨的定义,直接从实际理解、使用方式、常见坑这几个角度讲清楚。


这两个函数到底是干嘛的;一句话版本

在一个有序序列中:

  • lower_bound:找到 第一个 >= x 的位置

  • upper_bound:找到 第一个 > x 的位置

记住这句话基本就够用了。


举个最简单的例子

复制代码
vector<int> v = {1, 2, 2, 2, 3, 4};

查找 2:

复制代码
lower_bound(v.begin(), v.end(), 2) → 指向第一个 2  
upper_bound(v.begin(), v.end(), 2) → 指向第一个 3

也就是说:

复制代码
lower_bound → 左边界  
upper_bound → 右边界

返回的是什么;别搞错了

这两个函数返回的不是下标,而是:

迭代器(iterator)

如果你要下标,需要这样写:

复制代码
int pos = lower_bound(v.begin(), v.end(), x) - v.begin();

最常见用途一;判断元素是否存在

复制代码
auto it = lower_bound(v.begin(), v.end(), x);

if (it != v.end() && *it == x) {
    cout << "存在";
}

解释:

  • lower_bound 找到第一个 >= x 的位置

  • 如果这个位置刚好等于 x → 说明存在


最常见用途二;统计某个数出现次数

复制代码
int cnt = upper_bound(v.begin(), v.end(), x) 
        - lower_bound(v.begin(), v.end(), x);

这行代码非常经典,直接记住:

出现次数 = 右边界 - 左边界


最常见用途三;插入位置

如果你要往有序数组中插入一个元素:

复制代码
auto it = lower_bound(v.begin(), v.end(), x);
v.insert(it, x);

这样插入之后,数组依然有序。


必须注意的前提;一定要有序

这两个函数的前提是:

序列必须是有序的(默认升序)

如果是乱序数组:

  • 结果是错的

  • 但不会报错(这是最坑的地方)


自定义排序怎么办

如果你用的是自定义排序,比如降序:

复制代码
sort(v.begin(), v.end(), greater<int>());

那你就必须传入同样的比较函数:

复制代码
lower_bound(v.begin(), v.end(), x, greater<int>());

否则结果是错误的。


手写理解;它本质就是二分

可以用一个简单的"手写版本"来理解 lower_bound:

复制代码
int l = 0, r = n;
while (l < r) {
    int mid = (l + r) / 2;
    if (v[mid] >= x) r = mid;
    else l = mid + 1;
}

最终:

复制代码
l 就是 lower_bound 的位置

而 upper_bound 只是把条件改成:

复制代码
v[mid] > x

常见坑总结

; 忘了数组必须有序

很多人直接用在乱序数组上,结果完全不对

; 把返回值当下标

记住它返回的是迭代器

; 边界没判断

复制代码
it != v.end()

一定要写

; 自定义排序没传比较函数

这是中高级选手最容易翻车的地方


一句话记忆法

如果你懒得记一堆东西,就记这一句:

lower_bound 找"第一个不小于",upper_bound 找"第一个大于"


最后

这两个函数本质上就是"封装好的二分查找",但它们的威力在于:

  • 写法简洁

  • 不容易出错

  • 能直接解决很多区间问题

建议你多用几次,比如:

  • 统计区间

  • 去重处理

  • LIS(最长上升子序列)

用多了,自然就顺手了。

相关推荐
码爸2 小时前
线性回归算法
算法·机器学习·线性回归
Coder-magician2 小时前
《代码随想录》刷题打卡day51:图论-part09(dijistra堆优化,Bellman_ford算法)
算法·图论
Nil2082 小时前
leetcode 763划分字母区间
算法·leetcode·职场和发展
不会就选b3 小时前
算法日常・每日刷题--<动态规划>20
算法·leetcode·动态规划
别动我齐刘海3 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
青少儿编程课堂4 小时前
迪杰斯特拉(Dijkstra)算法:单源最短路与赛场路线规划
c++·python·算法·bfs·信息学竞赛
不会就选b4 小时前
算法日常・每日刷题--<动态规划>19
算法·leetcode·职场和发展
code2cat4 小时前
Java进阶篇之AtomicLongFieldUpdater:把累计额度留在对象字段中
java·cas·并发编程
WeiXin_DZbishe4 小时前
基于springboot城市公交实时运营与调度系统-计算机毕设 附源码【课程设计】66173
java·vue.js·spring boot·后端·python·php·课程设计
泡茶喝茶写代码4 小时前
A股量化数据工程:从 REST 接口到策略信号(第 18 篇):放量缩量异动扫描
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口