lower_bound找第一个≥target位置,upper_bound找第一个>target位置;均要求区间已排序,否则行为未定义;相比手写二分,stl版本避免了边界条件(如l/r更新、循环终止)等常见错误,保证正确性与高效性。

直接说结论: lower_bound 找的是“第一个 ≥ target”的位置,upper_bound 找的是“第一个 > target”的位置;两者必须作用于已排序区间,否则结果未定义。
为什么用 lower_bound 而不是手写二分?
手写二分最常出错的是边界条件:比如 l 还是 <code>l ,<code>mid 向下取整还是向上,更新时该写 l = mid + 1 还是 l = mid。稍有不慎就死循环或越界。lower_bound 把这些全封装好了,只暴露语义清晰的接口。
- 它要求输入区间是升序(默认用
比较),不满足则行为未定义——不是“可能错”,而是“一定不可靠” - 返回值是迭代器,不是下标;要转下标得显式做减法:
it - container.begin() - 若没找到(所有元素都 last,即容器末尾的“哨兵”迭代器,**不是空指针,也不等于
nullptr**
upper_bound 和 lower_bound 配合查重复元素范围
当你需要知道某个值在 vector 中出现几次、从哪开始到哪结束,lower_bound 和 upper_bound 是黄金组合。它们共同定义了一个左闭右开区间 [lower, upper),里面全是等于 target 的元素。
- 例如
vector<int> v = {1,3,5,7,7,7,9}</int>,查7:lower_bound返回索引 3,upper_bound返回索引 6 → 共 3 个 - 如果 target 不存在(如查
6),两者返回相同位置 → 区间为空,个数为 0 - 别误以为
upper_bound返回的是“最后一个 7 的位置”——它返回的是“第一个 8 的位置”,也就是 7 的右边界
自定义比较函数时 comp 参数怎么传?
当容器按降序排列,或按结构体字段排序时,必须传入 comp,且逻辑要和排序方式严格一致。常见错误是传错类型或反向写条件。
- 降序查
vector<int> v = {9,7,7,7,5,3,1}</int>,要找第一个 ≤ 7 的位置,得用greater<int>()</int>:lower_bound(v.begin(), v.end(), 7, greater<int>())</int> - 结构体排序后查找,比如按
.score升序,那comp必须是[](const auto& a, const auto& b) { return a.score ,不能写成 <code>> - 注意:
comp是二元谓词,签名必须是bool(First, Second),且语义上表示 “First 是否排在 Second 前面”
容易被忽略的细节:迭代器失效与容器限制
这两个函数本身不修改容器,但结果依赖容器当前状态。一旦容器被插入、删除、sort 或 resize,原有迭代器可能失效,lower_bound 的返回值也就不再有效。
- 它们只支持前向迭代器及以上(
vector、deque、array可用;list不推荐——虽满足前向,但随机访问退化为 O(n),失去二分意义) -
std::set和std::map自带lower_bound成员函数,比泛型版本更快(利用红黑树结构),优先用成员版 - 别对
std::unordered_set调用它们——无序容器不满足前提,结果完全不可预测
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











