LeetCode162.寻找峰值
难度:中等
目录
作者原始思路
求最大值法(官方解法一)
题目分析
- 1.题目给出:nums[-1] = nums[n] = -∞,所以,我们可以想象数组中是存在这两个元素的,而且这两个元素都是-∞的
- 2.题目提示中给出nums[i] != nums[i + 1],说明左右两个元素一定不相等
算法思想
- 1.由于左右两个元素一定不相等,当我们找到最大值的时候,其左右两个元素一定小于这个值,这个值就是我们想要的结果,直接返回其索引即可
代码实现
class Solution {
public int findPeakElement(int[] nums) {
if (nums.length == 1) {
return 0;
}else if (nums.length == 2) {
return nums[0] > nums[1] ? 0 : 1;
}
var max = Integer.MIN_VALUE;
var maxIndex = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
maxIndex = i;
}
}
return maxIndex;
}
// 改进版
public int findPeakElement(int[] nums) {
var maxIndex = -1;
var max = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
maxIndex = i;
}
}
return maxIndex
}
}
代码分析:
- 我们通过一次循环找到最大值及其下标,返回其小标即可
官方解法
爬坡算法(方法二)
算法思想
- 1.假设我们从任意一个位置出发,我们的目的是找到最高峰,所以,我们需要一直往高处走
- 2.如果发现下一个位置比当前位置大,即比当前位置高,我们就往下一个位置走
- 3.如果发现上一个位置比当前位置大,我们就往上一个位置走
- 4.如果发现上一个和下一个位置都比当前的位置小,说明我们到达了高峰,直接返回下标
- 5.如果发现上一个和下一个位置都比当前位置大,往哪走都可以
代码实现
class Solution {
public int findPeakElement(int[] nums) {
int step = (int) (Math.random() * nums.length);
while (!(compare(nums,step-1,step) < 0 && compare(nums,step,step+1) > 0)) {
if (compare(nums,step,step+1) < 0) {
step++;
}else {
step--;
}
}
return step;
}
public int[] get(int[] nums,int index) {
if (index == nums.length || index == -1) {
return new int[] {0,0};
}
return new int[] {1,nums[index]};
}
public int compare (int[] nums,int index1,int index2) {
var num1 = get(nums,index1);
var num2 = get(nums,index2);
if (num1[0] != num2[0]) {
return num1[0] - num2[0];
}else {
return num1[1] - num2[1];
}
}
}
代码分析:
- 1.compare()方法
- 1.1目的:用于比较两个位置的大小,即两个位置的高度,通过比较才可以知道往哪边走
- 1.2参数:nums(数组,便于访问里面的元素),index1(第一个元素的下标),index2(第二个元素的下标)
- 1.3get()方法
- 1.4如果两个数组的第一个元素不相等,说明有一个是-∞,有一个是有数值的,我们直接返回第一个元素之差即可,这样就可以比较大小
- 1.5如果两个数组的第一个元素相等,我们只需要返回第二个元素之差即可
- 1.6不懂的话看compareTo源码
- 2.get()方法
- 2.1当我们对元素进行比较的时候,对于边界,可能出现-1和nums.length的情况,由于题目中给出了这两个位置是-∞,所以我们要对这连个位置进行处理
- 2.2参数:nums(数组,便于访问里面的元素),index(所获取元素的下标)
- 2.3如果发现是特殊的两个位置,我们就返回new int[] {0,0}
- 2.4否则返回new int[] {1,nums[i]}
- 注意:0表示特殊情况,1表示普通情况
- 3.step表示的是站立位置对应的下标,我们可以调用radom方法,获取一个随机位置,随即开始走
- 4.循环
- 4.1若发现当前step的位置比左右位置都要大,没必要再去走了,直接结束循环
- 4.2根据思想完成代码即可,注意:这里其实隐藏了个策略,即如果到了谷底(比两边都小),他会优先向右走
问题;
- 如果我们仅仅按照上述算法去解决问题的话,最坏时间复杂度下,为O(N)线性阶,不符合题目要求我们完成的对数阶,所以,我们要对算法进行改造升级
爬坡算法升级版-二分实现
算法思想
- 我们从上面不妨可以看出一个隐藏的细节,即我们爬坡是没有回头路的,即不会往右走完后突然往左走,所以,我们可以引入二分算法
- 我们假设中间的地方就是我们开始的地方,利用二分的思想找出top,实现对数阶
代码实现
class Solution {
public int findPeakElement(int[] nums) {
var left = 0;
var right = nums.length - 1;
var top = -1;
while (left <= right) {
var mid = (left + right) / 2;
if (compare(nums,mid,mid+1) > 0 && compare(nums,mid,mid-1) > 0) {
top = mid;
break;
}
if (compare(nums,mid,mid+1) < 0) {
left = mid + 1;
}else {
right = mid - 1;
}
}
return top;
}
public int[] get(int[] nums,int index) {
if (index == nums.length || index == -1) {
return new int[] {0,0};
}
return new int[] {1,nums[index]};
}
public int compare (int[] nums,int index1,int index2) {
var num1 = get(nums,index1);
var num2 = get(nums,index2);
if (num1[0] != num2[0]) {
return num1[0] - num2[0];
}else {
return num1[1] - num2[1];
}
}
}
代码分析
- 1.我们需要动态获取中间mid的索引
- 2.先判断一下mid对应的元素是否是最高的地方,若是直接赋值给top,并直接退出循环
- 3.否则,如果mid+1大于mid的话,说明最高的地方一定在mid的右边,所以,在[mid+1,right]的范围内一定有最高点
- 4.如果mid-1大于mid的话,说明最高的地方一定在mid的左边,所以,在[left,mid-1]的范围内一定有最高点
- 5.根据以上两种情况对left和right做出更改即可
- 6.如果不会二分思想可以参考博主的数据结构与算法栏目
结论
该算法有爬坡和取最大值,更形象的是爬坡算法,但是代码量稍微有点大,取最大值虽有一些抽象,但是比较简单,两个算法都可以,但是比较推荐取最大值,该算法时间消耗的较少
最后总结必须要掌握的几点
1.爬坡算法的思想与进阶思想
2.取最大值的思想
3.代码实现