问题描述
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
对于所有有效的 i 都有 nums[i] != nums[i + 1]。
原题
思路与方法
对于此题很容易想到的方法是遍历数组,查看每一个元素是否为峰值,但时间复杂度是O(n),而题目要求O(logn),因为该题有任意相邻元素不相等并且nums[-1] = nums[n] = -∞ 这两个条件,可知一定存在峰值,并且对于任意一个元素,若相邻元素大于它则在大于它的元素方向上必然有峰值。以右边为例,若对于元素num[i]有num[i+1]>num[i],假设num[i]的右边没有峰值,则num[i+1]不为峰值,所以num[i+1]<num[i+2],num[i+2]也不为峰值,所以num[i+2]<num[i+3],以此类推直到num[n-2]<num[n-1],而num[n-1]是末尾元素,又有nums[n] = -∞,故num[n-1]>num[n]并且num[n-1]>num[n-2],即num[n-1]为峰值,与假设矛盾,故该方向上一定有峰值。利用此点可以用二分法降低时间复杂度,每次取区间中点,若为峰值直接返回,若左侧元素大则将区间缩至左半区间,继续二分,若右侧元素大则将区间缩至右半区间,循环此过程直到找到峰值。
Code
public int findPeakElement(int[] nums) {
int left=0,right=nums.length-1;
//int ans=0;
while(left<right){
int mid=(left+right)/2;
int before=mid>0?nums[mid-1]:Integer.MIN_VALUE;//左侧元素
int after=mid<nums.length-1?nums[mid+1]:Integer.MIN_VALUE;//右侧元素
if(nums[mid]>=before&&nums[mid]>=after){
return mid;
}else if(nums[mid]<before){
right=mid-1;
}
else if(nums[mid]<after){
left=mid+1;
}
}
return left;
}
复杂度分析
二分法时间复杂度是O(logn),内存上只用了常数量级的空间,故空间复杂度为O(1)