LeetCode琅琊榜第十二层-寻找峰值(爬坡算法)

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.代码实现

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 40
    评论
评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爪哇土著、JOElib

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值