用 std::upper_bound 最稳,它返回首个大于 target 的迭代器;手写需注意左闭右开区间、mid 防溢出(left + (right - left) / 2)、right = mid 而非 mid - 1,以及边界检查。

二分查找找首个大于 target 的元素怎么写
直接用 std::upper_bound 最稳,它返回第一个 > target 的迭代器。自己手写时容易把边界条件搞错,关键在「左闭右开」区间和循环退出后 left 的含义。
常见错误是写成 left 然后手动判断,结果越界或漏掉边界;或者混淆了「大于」和「大于等于」的逻辑。
- 推荐统一用左闭右开:
[left, right),初始化right = nums.size() - 循环中若
nums[mid] ,说明答案在右半段 → <code>left = mid + 1 - 否则
right = mid(不是mid - 1,因为 mid 可能就是答案) - 退出后
left就是首个 > target 的下标,注意检查是否越界:if (left == nums.size())表示全都不大于
找最后一个小于 target 的元素为什么不能直接改比较符
不能简单把「首个大于」的代码里 换成 <code>>= —— 语义和区间收缩方向都变了。本质是求满足 nums[i] 的最大 i,这对应的是「上界减一」,但更稳妥的是单独推导。
实际场景比如:数组 [1,2,2,3,4,4,5],target=3,要找最后一个
- 仍用左闭右开:
[left, right),right = nums.size() - 若
nums[mid] ,mid 合法,答案至少是 mid,所以 <code>left = mid + 1 - 若
nums[mid] >= target,mid 不合法,排除它:right = mid - 退出后
left - 1是最后一个 left > 0,否则无解
手写二分时最容易踩的三个坑
不是算法逻辑难,而是细节一错就无限循环或越界。尤其在变体中,这些点几乎必出问题。
-
mid计算写成(left + right) / 2→ 可能整型溢出,必须用left + (right - left) / 2 - 更新
right时用了right = mid - 1,但在左闭右开下,正确是right = mid;反之,左闭右闭时才用-1 - 忘记检查边界:比如找「首个大于」时没判
left == nums.size(),直接访问nums[left]就崩了
用 STL 时 lower_bound 和 upper_bound 到底怎么选
别死记「lower 是 >=,upper 是 >」——记住了也容易用反。关键是看你要什么位置:
- 要第一个 ≥ target →
lower_bound - 要第一个 > target →
upper_bound - 要最后一个 ≤ target →
upper_bound - 1(先找第一个 >,再往回一位) - 要最后一个 lower_bound - 1(因为
lower_bound是第一个 ≥,它前一位就是最后一个
注意:所有 STL 版本都要求输入是已排序区间,且传入的比较函数必须和排序一致;自定义类型务必重载 operator 或传 comparator。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











