峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组 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) 时间复杂度的。
解题思路:
查找+O(logN)一般而言都是二分法。本题中可以看出,峰值的定义:
1. 一个元素大于左右相邻元素。
2. 如果没有左邻居,只需大于右边。
3. 如果没有右邻居,只需大于左边。
因此,可以直接判断数组两端的元素是否存在峰值。
数值在数组中的状态:
1. 递增。nums[j-1]<nums[j]<nums[j+1]
2. 递减。nums[j-1]>nums[j]>nums[j+1]
3. 峰值。nums[j-1]<nums[j]>nums[j+1]
4. 低谷。nums[j-1]>nums[j]<nums[j+1]
假如峰值不在两端,那么必然是左端递增,右端递减。我们可以查询mid=(left+right)/2的位置的状态。
1. 如果mid递增,峰值必然出现在递增和递减之间的位置,因此left = mid,继续查询区间[mid,right]。
2. 如果mid递减,峰值必然出现在[left,mid]之间。
3. 如果mid为峰值,那么return mid;
4. 如果mid 为低谷,那么必然有两个峰值分别在mid左边和右边至少一个。本题只需查找一个峰值,因此选择其中一边ji即可。
class Solution { public: int findPeakElement(vector<int>& nums) { int size = nums.size(); if (size == 1) return 0; int left = 0, right = size - 1, mid; int sgn_L, sgn_R, sgn_M; while (left < right - 1) {//至少3个数 if (left + 2 == right) { return (nums[left]>nums[left + 1] ? left : (nums[right] > nums[right - 1] ? right : right - 1)); } if (nums[left] < nums[left + 1]) sgn_L = 1; if (nums[left] == nums[left + 1]) return left; if (nums[left] > nums[left + 1]) sgn_L = -1; if (nums[right]>nums[right - 1]) sgn_R = 1; if (nums[right] == nums[right - 1]) return right; if (nums[right] < nums[right - 1]) sgn_R = -1; mid = (left + right) / 2; if (nums[mid] >= nums[mid - 1] && nums[mid] >= nums[mid + 1]) return mid; if (nums[mid] >= nums[mid - 1] && nums[mid] <= nums[mid + 1]) { sgn_M = 1; if (sgn_L == -1) right = mid; else if (sgn_R == -1) left = mid; else return right; } if (nums[mid] <= nums[mid - 1] && nums[mid] >= nums[mid + 1]) { sgn_M = -1; if (sgn_L == 1) right = mid; else if (sgn_R == 1) left = mid; else return left; } if (nums[mid] <= nums[mid - 1] && nums[mid] <= nums[mid + 1]) { left = mid; } } return (nums[left]<nums[left + 1] ? left + 1 : left); } }; |