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.
Credits:
Special thanks to @ts for adding this problem and creating all test cases.
【题目分析】
在一个数组中任意两个相邻元素是不相同的,找到这样的元素,它比它的邻居节点都要大。如果存在这样的元素,返回其中任意一个即可。
【解体思路】
方法1:
遍历数组,
- 如果是最左边的元素,并且它比右邻居大,那它就是peak elmemnt。
- 如果是最右边的元素,并且它比左邻居大,也是peak element。
- 否则,如果它比左右邻居都大,那么它也是peak element。
代码如下:
1 class Solution 2 { 3 public: 4 int findPeakElement(vector<int>& nums) 5 { 6 if (nums.size() == 1) 7 { 8 return 0; 9 } 10 11 for (int i = 0; i < nums.size(); i++) 12 { 13 if ((i == 0 && nums[i] > nums[i + 1]) 14 || (i == nums.size() - 1 && nums[i] > nums[i - 1])) 15 { 16 return i; 17 } 18 19 if (0 < i && i < nums.size() - 1 20 && nums[i] > nums[i - 1] && nums[i] > nums[i + 1]) 21 { 22 return i; 23 } 24 } 25 26 return -1; 27 } 28 };
方法2:
上面的算法用到了很多次比较,下面给出一段更简单的代码:
1 class Solution 2 { 3 public: 4 int findPeakElement(vector<int>& nums) 5 { 6 // 对于nums[0]:题目规定它大于左邻居(不存在),因此只要它比右邻居大,那它就是peak element。 7 // 如果不是,那说明它不比右邻居大。而题目规定相邻节点不相等,因此它必然小于右邻居。 8 // 9 // 对于nums[1]:由上面的分析可知:左邻居小于当前节点。因此只要它比右邻居大,那就是peak element了。 10 // 如果也不是,那说明它也比右邻居小。 11 // ... 12 // 对于nums[sz - 2]:如果之前都没有返回。由上面规律可知:它比左邻居大。 13 // 而按照题目规定,它又一定大于右邻居(不存在)。因此它一定是peak element。 14 for (int i = 0; i < nums.size() - 1; i++) 15 { 16 if (nums[i] > nums[i + 1]) 17 { 18 return i; 19 } 20 } 21 return nums.size() - 1; 22 } 23 };
上面两个解法都能够被LeetCode接受,但是它们的时间复杂度都是是O(n)。而题目中要求是:O(log n)。
方法3:
由上面的分析,我们可以得出这样一个规律:
只要数组满足下面的形式:
[ 小,大,... 未知 ...,大,小 ]
那么其中一定能找到peak elememnt。
尝试用二分法
处理:
取数组的中间值mid
,
- 如果
mid
的值比mid+1
的值小那么从
mid
开始的数组右半部分将满足:[小,大,...,大,小]
的形式,所以其中一定有peak element。又因为
mid
肯定不是peak element,所以可以进一步把范围缩小为[mid+1, 数组末尾]
。 - 如果
mid
的值比mid+1
的值大
那么从数组的
开始位置
到mid+1
位置,也将构成[小,大,...,大,小]
的形式。而
mid+1
显然也不是peak element。所以可进一步把范围缩小到[数组开始,mid]
。
代码如下:
1 public class Solution { 2 public int findPeakElement(int[] nums) { 3 int left = 0, right = nums.length-1; 4 while(left < right){ 5 int mid = (right - left)/2 + left; 6 if(nums[mid] > nums[mid+1]) 7 right = mid; 8 else 9 left = mid + 1; 10 } 11 return left; 12 } 13 }