题目:
A peak element is an element that is greater than its neighbors.
Given an input array where num[i] ≠ num[i+1]
, find a peak element and return its index.
The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.
You may imagine that num[-1] = num[n] = -∞
.
For example, in array [1, 2, 3, 1]
, 3 is a peak element and your function should return the index number 2.
Note:
方法一:顺序查找 O(n),比较2n次
Your solution should be in logarithmic complexity.
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int n = nums.size();
if (nums.empty()) return -1;
for (int i = 1; i < n - 1; ++i){//寻找数组中间的peak
if (nums[i] > nums[i - 1] && nums[i] > nums[i + 1]) return i;
}
return nums[0] > nums[n-1] ? 0 : n-1;//数组中间没有peak,返回数组首尾的较大者(此时,数组的形状可能有3种:单调递增、单调递减、先单调递减再单调递增)
}
};
方法二:还是 O(n),但是仅比较n次
因为nums[-1] 为负无穷,所以nums[0]大于nums[-1],这是一个上升的趋势,接下来我们只要找到第一个出现下降趋势的点,即nums[i] < nums[i-1],即为peak,如果不存在这样的点,说明数组是单调递增的,而nums[n]为负无穷,所以nums[n-1]即为peak。
class Solution {
public:
int findPeakElement(vector<int>& nums) {
for (int i = 0; i < nums.size()-1; ++i){
if (nums[i] > nums[i + 1]) return i;
}
return nums.size() - 1;
}
};
class Solution {
public:
int findPeakElement(vector<int>& nums) {
if (nums.empty()) return -1;
int n = nums.size();
int index = nums[0] > nums[n - 1] ? 0 : n - 1;
findPeakElementCore(nums, 0, n-1, index);
return index;
}
private:
bool findPeakElementCore(vector<int> &nums, int start, int end, int &index){//寻找不在端点的peak
int len = end - start + 1;
if (len < 3) return false;
int mid = (start+end)>>1;
if (nums[mid] > nums[mid - 1]){
if (nums[mid] > nums[mid + 1]){//找到peak
index = mid;
return true;
}
else findPeakElementCore(nums, mid, end, index);//单调递增,在右边找,因为nums[n]为负无穷
}
else return findPeakElementCore(nums, start, mid, index);//单调递减,在左边找,因为nums[-1]为负无穷;或先递减再递增,此时任意一边找都可以因为nums[0]和nums[n]都为负无穷
}
};
非递归实现:
class Solution {
public:
int findPeakElement(vector<int>& nums) {
if (nums.empty()) return -1;
int n = nums.size();
int index = nums[0] > nums[n - 1] ? 0 : n - 1;
int left = 0, right = n - 1;
while (left < right){
int mid = (left + right) >> 1;
if (nums[mid] < nums[mid + 1]) left = mid+1;单调递增, 再右半边查找
else right = mid;//单调递减,在左半边查找
}
return left;//循环结束时left == right
}
};
注:本问题的实质是寻找第一个比它的后继大的点,若找到返回它的位置,若找不到(单调递增)返回最后一个位置。这样,该问题就简化为了一个普通的查找问题,并且可以用折半查找(即二分查找)以将时间复杂度降为O(logn)