leetcode162. 寻找峰值
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞。
示例 1:
输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,你的函数应该返回其索引 2。
示例 2:
输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5
解释: 你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
说明:
你的解法应该是 O(logN) 时间复杂度的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-peak-element
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解答
写在前面注意这道题的条件是nums[-1] = nums[n] = -∞ ,也就是说,左右两边是最小值,那么只要数组中有个值就肯定会有峰值,毕竟如果画个图像的话,肯定会出现那个转折点,高数忘得差不多了,应该是斜线为0,一次函数中的k。
解法1:可以直接遍历,因为左右两边都是最小值,所以第一个元素就是递增,那么只要有一个减小的元素,说明就出现了峰值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public int findPeakElement(int[] nums) { 3 for(int i=0;i<nums.length-1;i++) 4 { 5 if(nums[i]>nums[i+1]) 6 return i; 7 } 8 return nums.length-1; 9 } 10 }
也可以从两边同时开始,但是复杂度都是一样的,都是On,这又说明了,在一个(0~n)的循环中,如果你缩短循环次数,但是你每次循环要做的个数变成2倍的话,还是一样的,说明一次做多个但是只要是你都做了,就是并不会降低复杂度,只有某些元素直接跳过,这样才会降低复杂度。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public int findPeakElement(int[] nums) { 3 4 for(int i=0;i<nums.length-1;i++) 5 { 6 if(nums[i]>nums[i+1]) 7 return i; 8 if(nums[nums.length-1-i]>nums[nums.length-2-i]) 9 return nums.length-1-i; 10 } 11 return nums.length-1; 12 } 13 }
解法2:利用二分法,有关二分法的变种实在太多,每次都会又新的发现,你以为掌握的,其实实在是太少,
1.纯正的二分查找是利用数组有序而将双指针逼近查找有没有命中target,这是最初学习的,我一般喜欢在while循环里面判断条件
while(l<=r) 但是这样写要非常注意最后的结果究竟是l还是r,因为最后跳出循环的时候l,r是不相同的,所以这里要注意。
2.l,r,mid的关系,这三个的关系十分烦人且难搞,究竟l=mid+1,还是l=mid,还是说r=mid-1,r=mid,这都是需要考虑的,尤其是循环判断条件的l是不是包括等于r的情况,不然很有可能是跳不出循环的,一般来说l==r 是要配合mid+1,mid-1使用的,不然可能会出现l==r,然后一直死在这里,
一般l<r,应该是可以用mid+1,mid的,也即是可以不用强迫每一步都跳过边界。
这里提供两种的二分。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public int findPeakElement(int[] nums) { 3 int l=0; 4 int r=nums.length-1; 5 while(l<r) 6 { 7 int mid=l+(r-l)/2; 8 if(nums[mid]<nums[mid+1]) 9 l=mid+1; 10 else 11 r=mid; 12 } 13 return l; 14 } 15 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public int findPeakElement(int[] nums) { 3 int l=0; 4 int r=nums.length-1; 5 while(l<=r) 6 { 7 int mid=l+(r-l)/2; 8 if(mid<r&&nums[mid]<nums[mid+1]) 9 l=mid+1; 10 else if(mid>l&&nums[mid]<nums[mid-1]) 11 r=mid-1; 12 else 13 return mid; 14 } 15 return l; 16 } 17 }
总之,二分的条件还是比较多的,需要自己小心,要不要加等号,要不要+1,-1都是需要考虑的,还是需要根据题目的意思来吧,这道题主要是往中间夹。