Find Peak Element 寻找峰值
Find Peak
Element
给定一个数组,其中任意两个相邻元素的值不等,寻找数组中某峰值的index
i,使得n[i - 1] < n[i] > n[i + 1]
。保证峰值一定存在,如果有多个峰值,可返回任意一个。
乍看此题不难,O(n)的算法很容易想到,直接遍历数组即可。但题目要求给出O(log(n))的算法。显然,直观上应该用二分查找,否则复杂度要求不能满足。可数组是无序的,无法直接应用二分查找。
开始的想法是分情况讨论n[m]与n[l]和n[r]的关系,但发现三者之间的关系与峰值是否存在联系不起来。这里就引入了第一个tricky的点:考虑n[m]与n[m - 1]的关系。
- n[m] > n[m - 1]:
峰值必出现在右边,
l = m
。反证法,若右边的数组单调递增,则最右的元素是峰值(这是第二个tricky的点,最初没想到单调递增的数组中也必存在峰值);若右边的数组在某处开始递减,则峰值就在拐点。 - n[m] < n[m - 1]:
峰值必出现在左边,
r = m - 1
。同理可证。
算法有了,代码就有了?未尽然,还要考虑如下边界条件:
- 计算中点index
int m = l + (r - l) / 2;
可能出现l=m
的情况,处理不当导致死循环或下标越界。考虑到上述公式是倾向于选中点偏左的位置,那么使用n[m]与n[m+1]的关系可以让代码更简捷,在二分查找只剩下2个元素时,不会下标越界。 - 数组只有一个元素。在数组仅有一个元素时不必再计算中点,直接返回,因此循环停止条件为
l < r
而非l <= r
。
综合上述分析,代码如下,非常简捷:
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int l = 0, r = nums.size() - 1;
while(l < r)
{
int m = l + (r - l) / 2;
if (nums[m] > nums[m + 1]) r = m;
else l = m + 1;
}
return l;
}
};
此题对二分查找的考察比较全面,对边界条件的处理要求较高,分析复杂,代码短小精干。