题目描述
思路解析动画文字版
挨个检查每个数是不是比左右都大,O(n) 能做。但数组并不需要有序——只要懂「往高处走必有峰」,就能砍到 O(log n)。
转折:数组没排序,凭什么二分?凭「两端是负无穷 → 一定存在峰」这条保证。不变量:始终让搜索区间 [l, r] 里至少包含一个峰。怎么收?拿 nums[mid] 和右邻 nums[mid+1] 比:若 nums[mid] 上坡,右边继续走早晚到顶,峰在右(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 上坡上。顺着坡往上走早晚到顶,所以峰一定在 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 l == r == 5,区间只剩一个,循环结束。
答案:l 和 r 撞在下标 5,返回 5:nums[5]=6 比左邻 5、右邻 4 都大,确是峰。一路往高处爬,每轮砍一半,O(log n)。
没有 target、数组也不有序,照样能二分——只要存在「单调可判方向」。和右邻比,上坡向右、下坡收左含 mid,是这类「找极值」二分的通用思路。
参考代码
class Solution: def findPeakElement(self, nums): l, r = 0, len(nums) - 1 while l < r: mid = (l + r) // 2 if nums[mid] < nums[mid + 1]: l = mid + 1 else: r = mid return l复杂度
- 时间复杂度:O(log n),每轮把区间砍掉一半
- 空间复杂度:O(1),只用 l、r、mid 三个变量
可套用的代码模板
骨架记牢:while l 。山脉数组找峰顶、局部极值都是这套爬坡二分。
Python
l, r = 0, len(nums) - 1while l < r: mid = l + (r - l) // 2 if nums[mid] < nums[mid + 1]: l = mid + 1 # 上坡,峰在右 else: r = mid # 下坡,峰在 mid 或其左return l易错点
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
山脉数组的峰顶索引
LeetCode 852 · 中等 · 沿着 二分套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题