峰值元素是严格大于左右相邻元素的元素(边界元素只需大于唯一邻居),简单遍历时间复杂度为O(n),而题目要求O(log n),故需用二分查找;关键依据是nums[mid]与nums[mid+1]大小关系决定搜索方向。

什么是峰值元素,为什么不能用简单遍历
峰值元素指某个位置的值严格大于其左右邻居(边界元素只需大于唯一邻居即可)。很多人第一反应是写个 for 循环逐个比较,这确实能工作,但时间复杂度是 O(n)。而题目隐含要求是 O(log n),说明它期待你用二分查找——因为数组虽未整体有序,但“峰值一定存在”,且局部单调性允许二分收缩区间。
关键判断依据是:nums[mid] ,说明右侧有上升趋势,峰值必在右半段(包括 <code>mid + 1);反之则在左半段(包括 mid)。
用 std::lower_bound 或手写二分?别混淆
std::lower_bound 要求整个数组升序,不适用于峰值查找——它根本不知道“峰值”语义。必须手写二分逻辑,控制左右边界收缩方式。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 使用闭区间
[left, right]比开区间更直观,避免越界计算 - 每次比较
nums[mid]和nums[mid + 1](而非mid - 1),可避开mid == 0的边界检查 - 循环条件用
left ,退出时 <code>left == right就是答案位置 - 当
nums[mid] ,设 <code>left = mid + 1;否则right = mid
边界处理和常见错误
最容易错的是访问越界:比如用 nums[mid - 1] 和 nums[mid + 1] 同时比较,没检查 mid == 0 或 mid == nums.size() - 1,直接触发 std::out_of_range 或未定义行为。
正确做法是只依赖单侧比较:
- 只比
nums[mid]和nums[mid + 1],这样mid最大取到n - 2,所以初始化right = nums.size() - 2(闭区间下) - 或者统一用
mid = left + (right - left) / 2,并确保right初始为n - 1,但比较时用mid + 1 做保护——不过会增加分支,不如前者干净 - 输入为空数组?题目通常保证非空,但生产代码里仍应先断言
!nums.empty()
一个可靠、可直接粘贴的实现
int findPeakElement(const std::vector<int>& nums) {
int left = 0, right = nums.size() - 2; // 保证 mid+1 不越界
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < nums[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
这个版本不依赖 nums[-1] 或 nums[n] 的虚构值,逻辑清晰,边界安全。注意它返回的是任意一个峰值位置,不是所有峰值——如果需要全部,那就没法 O(log n),得回归线性扫描。
真正容易被忽略的是:二分能成立,不是因为数组有序,而是因为“只要往高处走,就一定能走到峰顶”。这个直觉比代码本身更重要。


















