162. 寻找峰值

中等 含交互动画

学完这道你应该能: 说清它为什么用「找峰值」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

找出任意一个峰值的下标(峰值 = 比左右邻居都大)。可假设两端之外是负无穷,所以一定存在峰值。

nums = [1,2,1,3,5,6,4]
输出 = 5(值 6 是峰)

思路解析

挨个检查每个数是不是比左右都大,O(n) 能做。但数组并不需要有序——只要懂「往高处走必有峰」,就能砍到 O(log n)。

转折:数组没排序,凭什么二分?凭「两端是负无穷 → 一定存在峰」这条保证。不变量:始终让搜索区间 [l, r] 里至少包含一个峰。怎么收?拿 nums[mid] 和右邻 nums[mid+1] 比:若 nums[mid] < nums[mid+1],当前在上坡,右边继续走早晚到顶,峰在右(l=mid+1);若 nums[mid] > nums[mid+1],当前在下坡,mid 自己可能就是峰,峰在 mid 或其左(r=mid)。一直往高处走,必撞上峰。

准备 · 闭区间 [0,6]:范围是整个数组,l=0、r=6。每轮都拿 nums[mid] 和它的右邻 nums[mid+1] 比,判断往哪边爬。

第 1 轮 · 取 mid:中点 mid=3,nums[3] = 3。和右邻 nums[4]=5 比,看是上坡还是下坡。

第 1 轮 · 判断:3 < 5,右边更高,说明现在站在上坡上。顺着坡往上走早晚到顶,所以峰一定在 mid 右边,mid 自己不是峰。

第 1 轮 · 收缩:l 跳到 mid+1=4,左半(灰)丢掉。范围缩到 [4, 6],继续往高处爬。

第 2 轮 · 取 mid(负例分支):范围 [4,6],新中点 mid=5,nums[5] = 6。和右邻 nums[6]=4 比——这轮会走到另一个分支

第 2 轮 · 判断:6 > 4,右边更低,说明已经翻过峰、在下坡了。那 mid 自己可能就是峰,峰在 mid 或它左边,不能丢 mid。

第 2 轮 · 收缩:r 收到 mid=5(不是 mid−1,因为 mid 自己可能就是峰)。下标 6 丢掉,范围 [4, 5]。

第 3 轮 · 取 mid:范围 [4,5],mid=4,nums[4] = 5。和右邻 nums[5]=6 比。

第 3 轮 · 收缩 → l==r:5 < 6,又是上坡,峰在右,l 跳到 mid+1=5。现在 l == r == 5,区间只剩一个,循环结束。

答案:l 和 r 撞在下标 5,返回 5:nums[5]=6 比左邻 5、右邻 4 都大,确是峰。一路往高处爬,每轮砍一半,O(log n)。

没有 target、数组也不有序,照样能二分——只要存在「单调可判方向」。和右邻比,上坡向右、下坡收左含 mid,是这类「找极值」二分的通用思路。

参考代码(Python)

Python
1def findPeakElement(self, nums):
2    l, r = 0, len(nums) - 1         # 闭区间 [l, r]
3    while l < r:                    # 注意是 < 不是 <=
4        mid = l + (r - l) // 2
5        if nums[mid] < nums[mid + 1]:  # 上坡,峰在右
6            l = mid + 1            # 往高处走
7        else:                      # 下坡,峰在 mid 或其左
8            r = mid                # mid 可能是峰,别丢
9    return l                        # l==r 时即峰

复杂度分析

  • 时间复杂度:O(log n) —— 每轮把区间砍掉一半
  • 空间复杂度:O(1) —— 只用 l、r、mid 三个变量

套路模板

骨架记牢:while l < r、和 nums[mid+1] 比、上坡 l=mid+1 否则 r=mid。山脉数组找峰顶、局部极值都是这套爬坡二分。

Python
1l, r = 0, len(nums) - 1
2while l < r:
3    mid = l + (r - l) // 2
4    if nums[mid] < nums[mid + 1]:
5        l = mid + 1        # 上坡,峰在右
6    else:
7        r = mid            # 下坡,峰在 mid 或其左
8return l

易错点

  • 错误写法:循环写成 while l <= r正确写法:while l < r(l==r 时 mid 可能取到末尾,nums[mid+1] 直接越界;用 < 让 l==r 收尾返回 l,从根上避开越界)
  • 错误写法:nums[mid] > nums[mid+1] 时写 r = mid - 1正确写法:r = mid(不减 1)(下坡时 mid 自己可能就是峰,减 1 会把峰丢掉)
  • 错误写法:想找「全局最大」才停正确写法:找到任意局部峰即可(题目只要任意一个峰,爬坡到的第一个峰就是答案,不必继续找更大的)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →215. 数组中第 K 个最大元素 ← 返回题库