前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >高频面试题:找出峰值元素

高频面试题:找出峰值元素

作者头像
你好戴先生
发布2022-05-16 15:33:03
4840
发布2022-05-16 15:33:03
举报
文章被收录于专栏:戴言泛滥

大家好,我是戴先生 今天给大家介绍一下如何利用玄学二分法找出峰值元素 想直奔主题的可直接看思路3 题目 给定一个整数数组 求出数组中任一峰值元素的索引地址i 注意: 1、峰值元素是指其值严格大于左右相邻值的元素 2、对于所有有效的 i 都有 nums[i] != nums[i + 1] 3、如果存在多个峰值元素,返回任一峰值元素索引即可

题解

根据题目,峰值元素其实就是将数组转换为坐标轴函数之后的极大值

可以简单地归为以下三种情况

第一种情况就是数组是单调递增或递减的

这种情况只存在一个峰值元素,为数值7

第二种情况数组先单调递减(增),再单调递增(减)

这种情况存在两(一)个峰值元素

为数值7,7(5)

第三种情况数组存在多个单调递增和递减的区间

这种情况存在多个峰值元素

为数值4,7,4

思路1

高中数学告诉我们:最大值一定是极大值

所以很简单了

只要求出数组中的最大值就可以了

求出最大值的方法就多了

最简单的就是遍历所有元素

代码实现1

思路1的代码实现如下

代码语言:javascript
复制
    public static int findPeekElement1(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int result = 0;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > nums[result]) {
                result = i;
            }
        }

        return result;
    }

思路2

在思路1的基础上我们可以做一下优化

题目要求我们找出任一极大值即可

所以我们在遍历数组的时候

只要找到了符合条件(大于左边和右边的值)的数

就能直接返回结果了

唯一需要注意的是首、尾两个值的情况

因为这两个值是没有“左边”或“右边”的值

所以如果在除了首尾之外的元素中没有找到符合条件的值

只需要比较一下首尾两个值,较大者即为极大值,且是最大值

代码实现2

思路2的代码实现如下

代码语言:javascript
复制
    public static int findPeekElement1(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        for (int i = 1; i < nums.length - 1; i++) {
            if (nums[i] > nums[i - 1] && nums[i] > nums[i + 1]) {
                return i;
            }
        }
        return nums[0] >= nums[nums.length - 1] ? 0 : nums.length - 1;
    }

思路3

思路1和思路2的时间负责都都是O(n)

那么有没有办法将时间复杂度优化一下呢?

给大家说一个思路

凡是遇到有序数据或有规律的区间有序数组

第一个可以尝试的方法就是玄学算法:二分法

如图所示

通过二分法找到第一个中间值mid的时候

如果nums[mid -1] < nums[mid]

那么mid或mid的右侧一定存在一个极大值

那么就可以将范围锁定到mid~end之间

同理

如果如果nums[mid -1] > nums[mid]

那么在mid或者mid的左侧必然存在一个极大值

那么就可以将范围锁定到start~mid之间

思路明确之后代码就很好实现了

而且二分算法的时间复杂度为log(n)

代码实现3

思路3的代码实现如下

代码语言:javascript
复制
    public static int findPeekElement2(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start + 1 < end) {
            mid = (start + end) / 2;
            if (nums[mid - 1] > nums[mid]) {
                end = mid;
            } else if (nums[mid + 1] > nums[mid]) {
                start = mid;
            } else {
                return mid;
            }
        }
        return nums[start] > nums[end] ? start : end;
    }

测试验证

以上分别是三个方法的时间和空间消耗对比

明显二分法再次胜出

文/戴先生@2022年05月15日

---end---

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 你好戴先生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题解
    • 思路1
      • 代码实现1
        • 思路2
          • 代码实现2
            • 思路3
              • 代码实现3
              • 测试验证
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档