问题
峰值元素是指其值大于左右相邻值的元素。
给你一个输入数组nums
,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值 所在位置即可。
你可以假设nums[-1] = nums[n] = -∞
提示:
1 <= nums.length <= 1000
-2^31 <= nums[i] <= 2^31 - 1
对于所有有效的 i 都有 nums[i] != nums[i + 1]
算法:二分 $O(logN)$
对应基础课整数二分。
回忆yxc老师讲二分的时候提到过:二分的本质在于,二分出来的那个点,会使得左右两边的区间具有不同的性质。
当然,这个本质严格来说只适用于二分单调的区间。但这并不意味着二分不单调的区间这个本质就完全失效了,事实上,这个本质对于二分不单调区间是部分正确的,区别在于只对局部区间成立,而不是对全部的区间都成立。那所谓局部区间到底是什么意思呢?很简单,分界点左右邻近的区间就是这个所谓的局部区间。
本题就是一个非常好的例子。区间虽然不单调,但是我们正好可以用上面提到的本质。为什么? 因为这道题核心就是要找一个点,这个点的左边是局部单调递增,右边是局部单调递减的 – 这正好迎合二分不单调区间的特质。那么具体代码怎么写呢?
其实就是抓住这道题的核心:找一个点,这个点的左边是局部单调递增,右边是局部单调递减的,也就是说,二分点的左邻区间递增,右邻区间递减,抓住了这一分界特点,代码就好写了:mid = l + r + 1 >> 1
是中点,如果nums[mid] > nums[mid-1]
, 则l = mid
, 表示要让分界点的左邻区间递增;反之则r = mid + 1
, 表示要让分界点的右邻区间递减。如果左邻递增区间把分界点逼到n-1,或者右邻递减区间把分界点逼到0,怎么办呢?由于题目保证nums[-1] = nums[n] = -∞
,所以分界点恰好就可以是n-1/0。
至于为什么mid
是那样计算,为什么r
加1而l
不加1,其实都是边界情况,原因全在整数二分这道题的视频讲解里了,不会的赶紧去复习!!
代码
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int l = 0, r = nums.size()-1;
while(l < r){
int mid = l + r + 1 >> 1;
if(nums[mid] > nums[mid-1]) l = mid;
else r = mid - 1;
}
return l;
}
};