寻找峰值[中等]

news2024/9/30 11:39:07

在这里插入图片描述

优质博文IT-BLOG-CN

一、题目

峰值元素是指其值严格大于左右相邻值的元素。给你一个整数数组nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设nums[-1] = nums[n] = -∞

你必须实现时间复杂度为O(log n)的算法来解决此问题。

示例 1:
输入:nums = [1,2,3,1]
输出:2
解释:3是峰值元素,你的函数应该返回其索引2

示例 2:
输入:nums = [1,2,1,3,5,6,4]
输出:15
解释:你的函数可以返回索引1,其峰值元素为2;或者返回索引5, 其峰值元素为6

提示:
1 <= nums.length <= 1000
-231 <= nums[i] <= 231 - 1
对于所有有效的i都有nums[i] != nums[i + 1]

二、代码

方案一:寻找最大值

由于题目保证了nums[i]≠nums[i+1],那么数组nums中最大值两侧的元素一定严格小于最大值本身。因此,最大值所在的位置就是一个可行的峰值位置。

我们对数组nums进行一次遍历,找到最大值对应的位置即可。

class Solution {
    public int findPeakElement(int[] nums) {
        int idx = 0;
        for (int i = 1; i < nums.length; ++i) {
            if (nums[i] > nums[idx]) {
                idx = i;
            }
        }
        return idx;
    }
}

时间复杂度: O(n),其中n是数组nums的长度。
空间复杂度: O(1)

方案二:二分查找

首先要注意题目条件,在题目描述中出现了nums[-1] = nums[n] = -∞,这就代表着 只要数组中存在一个元素比相邻元素大,那么沿着它一定可以找到一个峰值。

根据上述结论,我们就可以使用二分查找找到峰值

查找时,左指针l,右指针r,以其保持左右顺序为循环条件

根据左右指针计算中间位置m,并比较mm+1的值,如果m较大,则左侧存在峰值,r = m,如果m + 1较大,则右侧存在峰值,l = m + 1

class Solution {
    public int findPeakElement(int[] nums) {
        int left = 0, right = nums.length - 1;
        for (; left < right; ) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[mid + 1]) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}

时间复杂度: O(log⁡n),其中n是数组nums的长度。
空间复杂度: O(1)

方案三:迭代爬坡

俗话说「人往高处走,水往低处流」。如果我们从一个位置开始,不断地向高处走,那么最终一定可以到达一个峰值位置。

因此,我们首先在[0,n)的范围内随机一个初始位置i,随后根据nums[i−1],nums[i],nums[i+1]三者的关系决定向哪个方向走:
【1】如果nums[i−1]<nums[i]>nums[i+1],那么位置i就是峰值位置,我们可以直接返回i作为答案;
【2】如果nums[i−1]<nums[i]<nums[i+1],那么位置i处于上坡,我们需要往右走,即i←i+1

如果nums[i−1]>nums[i]>nums[i+1],那么位置i处于下坡,我们需要往左走,即i←i−1

如果nums[i−1]>nums[i]<nums[i+1],那么位置i位于山谷,两侧都是上坡,我们可以朝任意方向走。

如果我们规定对于最后一种情况往右走,那么当位置i不是峰值位置时:
【1】如果nums[i]<nums[i+1],那么我们往右走;
【2】如果nums[i]>nums[i+1],那么我们往左走。

class Solution {
    public int findPeakElement(int[] nums) {
        int n = nums.length;
        int idx = (int) (Math.random() * n);

        while (!(compare(nums, idx - 1, idx) < 0 && compare(nums, idx, idx + 1) > 0)) {
            if (compare(nums, idx, idx + 1) < 0) {
                idx += 1;
            } else {
                idx -= 1;
            }
        }
        
        return idx;
    }

    // 辅助函数,输入下标 i,返回一个二元组 (0/1, nums[i])
    // 方便处理 nums[-1] 以及 nums[n] 的边界情况
    public int[] get(int[] nums, int idx) {
        if (idx == -1 || idx == nums.length) {
            return new int[]{0, 0};
        }
        return new int[]{1, nums[idx]};
    }

    public int compare(int[] nums, int idx1, int idx2) {
        int[] num1 = get(nums, idx1);
        int[] num2 = get(nums, idx2);
        if (num1[0] != num2[0]) {
            return num1[0] > num2[0] ? 1 : -1;
        }
        if (num1[1] == num2[1]) {
            return 0;
        }
        return num1[1] > num2[1] ? 1 : -1;
    }
}

时间复杂度: O(n),其中n是数组nums的长度。在最坏情况下,数组nums单调递增,并且我们随机到位置0,这样就需要向右走到数组nums的最后一个位置。
空间复杂度: O(1)

方法四:二分查找优化

我们可以发现,如果nums[i]<nums[i+1],并且我们从位置i向右走到了位置i+1,那么位置i左侧的所有位置是不可能在后续的迭代中走到的。

这是因为我们每次向左或向右移动一个位置,要想「折返」到位置i以及其左侧的位置,我们首先需要在位置i+1向左走到位置i,但这是不可能的。

并且根据方法二,我们知道位置i+1以及其右侧的位置中一定有一个峰值,因此我们可以设计出如下的一个算法:
【1】对于当前可行的下标范围[l,r],我们随机一个下标i
【2】如果下标i是峰值,我们返回i作为答案;
【3】如果nums[i]<nums[i+1],那么我们抛弃[l,i]的范围,在剩余[i+1,r]的范围内继续随机选取下标;
【4】如果nums[i]>nums[i+1],那么我们抛弃[i,r]的范围,在剩余[l,i−1]的范围内继续随机选取下标。

在上述算法中,如果我们固定选取i[l,r]的中点,那么每次可行的下标范围会减少一半,成为一个类似二分查找的方法,时间复杂度为 O(log⁡n)

class Solution {
    public int findPeakElement(int[] nums) {
        int n = nums.length;
        int left = 0, right = n - 1, ans = -1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (compare(nums, mid - 1, mid) < 0 && compare(nums, mid, mid + 1) > 0) {
                ans = mid;
                break;
            }
            if (compare(nums, mid, mid + 1) < 0) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return ans;
    }

    // 辅助函数,输入下标 i,返回一个二元组 (0/1, nums[i])
    // 方便处理 nums[-1] 以及 nums[n] 的边界情况
    public int[] get(int[] nums, int idx) {
        if (idx == -1 || idx == nums.length) {
            return new int[]{0, 0};
        }
        return new int[]{1, nums[idx]};
    }

    public int compare(int[] nums, int idx1, int idx2) {
        int[] num1 = get(nums, idx1);
        int[] num2 = get(nums, idx2);
        if (num1[0] != num2[0]) {
            return num1[0] > num2[0] ? 1 : -1;
        }
        if (num1[1] == num2[1]) {
            return 0;
        }
        return num1[1] > num2[1] ? 1 : -1;
    }
}

时间复杂度: O(log⁡n),其中n是数组nums的长度。
空间复杂度: O(1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1485233.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决:Information:java: javacTask: 源发行版 8 需要目标发行版 1.8

解决&#xff1a;Information:java: javacTask: 源发行版 8 需要目标发行版 1.8 先点击 Project Structure 查看jdk是否为1.8版本 我这jdk版本为1.8版本的&#xff0c;但还是运行还是报错 据以上错误显示以及上述配置&#xff0c;我选择的编译器是jdk1.8的&#xff0c;但是在i…

Git LFS Github 提交大文件

问题背景 2024-03-03 一篇微服务系统测试工具的论文&#xff0c;事后发现自己一开始的理解有问题&#xff0c;导致自己算的预期值和论文中正确的值不一样。 测试工具原型的依赖定义有缺失&#xff0c;默认可能会用当前最新版&#xff0c;但时过境迁”最新版“变化会导致不兼容…

4款塞纸条盲盒交友源码,可以对接公众号

一元盲盒交友源码/脱单盲盒源码/交友盲盒/恋爱盲盒公众号版 可以对接自己支付&#xff0c;全部自定义 没有任何bug版本&#xff0c;已经测试完全可以 免费源码&#xff0c;不包搭建指导 源码下载地址专业知识分享社区-专业知识笔记免费分享 (chaobiji.cn)

springcloud:3.3测试重试机制

服务提供者【test-provider8001】 Openfeign远程调用服务提供者搭建 文章地址http://t.csdnimg.cn/06iz8 相关接口 测试远程调用&#xff1a;http://localhost:8001/payment/index 服务消费者【test-consumer-resilience4j8004】 Openfeign远程调用消费者搭建 文章地址http:/…

Git与GitHub:解锁版本控制的魔法盒子

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

计算机设计大赛 深度学习机器视觉车道线识别与检测 -自动驾驶

文章目录 1 前言2 先上成果3 车道线4 问题抽象(建立模型)5 帧掩码(Frame Mask)6 车道检测的图像预处理7 图像阈值化8 霍夫线变换9 实现车道检测9.1 帧掩码创建9.2 图像预处理9.2.1 图像阈值化9.2.2 霍夫线变换 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分…

MyBatis 学习(七)之 缓存

目录 1 MyBatis 缓存介绍 2 一级缓存 3 二级缓存 3.1 二级缓存介绍 3.2 二级缓存配置 3.3 二级缓存测试 4 参考文档 1 MyBatis 缓存介绍 MyBatis 缓存是 MyBatis 中的一个重要特性&#xff0c;用于提高数据库查询的性能。MyBatis 提供了一级缓存和二级缓存两种类型的缓存…

数据结构:栈和队列的实现附上源代码(C语言版)

目录 前言 1.栈 1.1 栈的概念及结构 1.2 栈的底层数据结构选择 1.2 数据结构设计代码&#xff08;栈的实现&#xff09; 1.3 接口函数实现代码 &#xff08;1&#xff09;初始化栈 &#xff08;2&#xff09;销毁栈 &#xff08;3&#xff09;压栈 &#xff08;4&…

求最短路径之BF算法

介绍 全称Bellman-Ford算法&#xff0c;目的是求解有负权边的最短路径问题。 考虑环&#xff0c;根据环中边的边权之和的正负&#xff0c;将环分为零环、正环、负环。其中零环、正环不会影响最短路径的求解&#xff0c;而负环会影响最短路径的求解。 可用BF算法返回一个bool值…

java开发环境配置一指禅

IDEA下载与安装 IDEA 全称 IntelliJ IDEA&#xff0c;是java编程语言的集成开发环境。 idea下载地址 。 JDK安装配置 JDK是 Java 语言的软件开发工具包&#xff0c;主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心&#xff0c;它包含了JAVA的运行环…

vue项目获取拼音首字母

工具包 pinyin-pro npm install pinyin-pro 官方地址 pinyin-pro | pinyin-pro性能优异、转换准确的 js 中文转拼音工具https://pinyin-pro.cn/示例代码(获取每个汉字的拼音首字母) import {pinyin} from pinyin-pro;function getPinyinInitial(name){if (name) {let py p…

aop监控spring cloud接口超时,并记录到数据库

引入pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0…

PlantUML简介

PlantUML简介 plantUML是一款开源的UML图绘制工具&#xff0c;支持通过文本来生成图形&#xff0c;使用起来非常高效。可以支持时序图、类图、对象图、活动图、思维导图等图形的绘制。你可以在IDEA中安装插件来使用PlantUML, 或者在Visual Studio Code中安装插件。 也可以在dra…

2024免费mac苹果电脑的清理和维护软件CleanMyMac X

对于 Mac 用户来说&#xff0c;电脑的清理和维护是一件让人头疼的事情。但是&#xff0c;有了 CleanMyMac X&#xff0c;这一切都将变得轻松愉快。CleanMyMac X 是一款专为 Mac 设计的电脑清理软件&#xff0c;它以其强大的功能和简单的操作&#xff0c;让无数用户为之倾倒。 C…

C语言学习DAY22

内存泄漏&#xff1a;开辟的内存空间不使用了&#xff0c;但不释放该内存空间。

AutoSAR(基础入门篇)13.3-Mcal Dio配置

目录 一、Dio port配置 二、Dio pin配置 一、Dio port配置 同之前的Port一样,双击进入Dio配置界面后会看到几乎差不多的配置界面。General和Port类似,我们不再赘述,主要讲解Dio的配置 1. 其实Dio并没有什么实质的作用,主要起到了一个重命名的功能。双击DioConfig_0进入下…

spring项目单元测试

单元测试 每个功能开发完&#xff0c;项目上线前都要做单元测试 引入依赖 AssertJ&#xff1a;断言 测试用例 编写测试用例要求&#xff1a;保证测试方法的独立性 当前测试方法不能依赖于别的测试方法&#xff0c;也不依赖于数据库中某条特定数据。 如A方法插入一条数据&a…

在网页上踢球:打造我自己的python(Django)足球网站

足球不仅仅是球场上的90分钟。它是一个不断发展的故事&#xff0c;一个全球球迷社群的粘合剂&#xff0c;一个数据和热情交织的世界。作为一名开发者和球迷&#xff0c;我决定将这两大爱好结合起来&#xff0c;用 Django 打造一个足球网站&#xff0c;让球迷们能够追踪他们最爱…

LeetCode --- 三数之和

题目描述 三数之和 代码解析 暴力 在做这一道题的时候&#xff0c;脑海里先想出来的是暴力方法&#xff0c;一次排序&#xff0c;将这个数组变为有序的&#xff0c;再通过三次for循环来寻找满足条件的数字&#xff0c;然后将符合条件的数组与之前符合条件的数组进行一一对比…

YOLOv9独家原创改进|使用HWD:Haar小波下采样模块

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、论文简介 最大池化或跨步卷积等下采样操作在卷积神经网络&#xff08;CNNs&#xff09;中广泛使用&#xff0c;以聚合局部特征&#xff0c;扩大感…