题目描述:峰值元素指的是其值严格大于左右相邻的元素。给定一个整数数组,其可能包含多个峰值元素,返回任意一个峰值元素的下标。(可认为nums[-1] = nums[n] = -∞
)
要求解法达到 l o g ( n ) log(n) log(n)时间复杂度。
简单想法就是遍历一次,对每个元素判断是否是峰值即可。但是时间复杂度为 O ( n ) O(n) O(n)。要想达到 l o g ( n ) log(n) log(n)的复杂度,只能采用类似二分的思想,每次排除掉一半的元素才行。接下来就想怎么二分呢。
nums[n]
为负无穷)nums[-1]
为负无穷)由于只需要找到一个峰值即可,那么只需要进行二分,每次往一定存在峰值的一端走即可(往更高处走)
我自己的代码(加了太多边界特判了,后来发现其实并不需要)
class Solution {
public int findPeakElement(int[] nums) {
return find(nums, 0, nums.length - 1);
}
private int find(int[] nums, int l, int r) {
if (l > r) return -1;
if (l == r) {
if (l == 0 && l == nums.length - 1) return l;
if (l == 0) return nums[0] > nums[1] ? 0 : -1;
if (l == nums.length - 1) return nums[l] > nums[l - 1] ? l : -1;
return nums[l] > nums[l - 1] && nums[l] > nums[l + 1] ? l : -1;
}
int mid = l + r >> 1;
if (mid == 0) {
if (nums[0] < nums[1]) return find(nums, mid + 1, r);
else return 0;
}
if (mid == nums.length - 1) {
if (nums[mid] < nums[mid - 1]) return find(nums, l, mid - 1);
else return mid;
}
// 4种情况
if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) return mid;
if (nums[mid] < nums[mid - 1] && nums[mid] < nums[mid + 1]) return find(nums, mid + 1, r);
if (nums[mid] > nums[mid - 1] && nums[mid] < nums[mid + 1]) return find(nums, mid + 1, r);
if (nums[mid] < nums[mid - 1] && nums[mid] > nums[mid + 1]) return find(nums, l, mid - 1);
return -1;
}
}
看完题解后,发现只需要简单二分就行了,10行代码左右
class Solution {
public int findPeakElement(int[] nums) {
int l = 0, r = nums.length - 1;
// 若只有一个元素, 则直接跳出循环
while (l < r) {
int mid = l + r >> 1; // 若只有2个元素, 保证中点取到左侧0
if (nums[mid] > nums[mid + 1]) r = mid;
else l = mid + 1;
}
return l;
}
}
总结:二分,每次都往更高的一端走(人往高处走),一定能找到一个峰值。