《LeetCode》—— 摆动序列

news2024/9/21 18:45:25

今天,我们要讲解的是 “摆动序列” 这道题目。对于这道题目,我们可以从贪心的思想去解决,也可以使用动态规划的方法。接下来,我通过这两种方法的讲解让你轻松拿捏它!


目录

(一)贪心算法

 1、上下坡中有平坡

2、 数组首尾两端

3、 单调坡中有平坡

(二)动态规划


 

题目链接:摆动序列

题目如下:

 

(一)贪心算法

题意分析:

摆动序列的意思就是相邻两个元素的差值要保持一正一负这种情况,只有这样数组中的所有元素的分布才是呈现出摆动的情况。

首先,我先把示例 1给大家展开解释一下:

 

💨 而本题要求是从原始序列中来获得子序列,剩下的元素保持其原始顺序。这就可能会要求删除元素使其达到最大摆动序列的情况,应该“删除”什么元素呢?


1、首先,我们一步步的来考虑,假如题目给出的数组只有两个元素,并且两元素并不相同,那么此时有几个呢? 

  • 答案是不是有两个摆动的序列呀!就如下图所表示的那样

 2、其次,对于数组中的元素大于两个的情况,我们有需要额外的去判定,别着急,我们通过题目中给出的例二分析一下在要求“删除”的情况下我们到底需要怎么做:

 通过上述的观察,我们就可以发现本题的思路其实可以从局部到整体:

  • 局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值
  • 整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列

因此,我们可以怎么做呢?

注意: 

  1.  因此,本道题的整体思路就出来了,当遇到元素摆动的时候,遇到摆动时 【Count++】,当遇到单调坡上的元素时,我们就做相应的处理不进行【Count++】操作。
  2. 对于我给出的“删除”,并不是真正意义上的删除,我们只需利用条件判断绕开在单调坡上的元素即可。如果大家一股脑的陷入 删除去做的话,会显得很复杂的!

接下来,我们就去看看如何判断摆动条件:

 这是我们思考本题的一个大题思路,但本题要考虑三种情况:

  1. 上下坡中有平坡
  2. 数组首尾两端
  3. 单调坡中有平坡


 1、上下坡中有平坡

对于上述的例二,相邻元素之间的值是不同的,但是是不是存在这样的一种情况,就是数组中相邻元素之间有相同的情况。

例如 [1,2,2,2,1]这样的数组,如图(把图画出来中间就出现了直平的情况):

 对于上图这种情况它的摆动序列长度是多少呢? 其实是长度是3,也就是我们在删除的时候 要不删除左面的三个2,要不就删除右边的三个2。

 所以,综上所述,我们记录峰值的条件应该是:

(prediff <= 0 && laterdiff > 0) || (prediff >= 0 && laterdiff < 0)

 


2、 数组首尾两端

因为题目给出收尾元素在计算时都是要算坡度的对其,那么我们在处理时也需要对其做相应的处理动作。

在上述的计算方法中,我们通过当前元素,以及当前元素的前一个元素和当前元素的后一个元素去求取两边的差值来判断,但是当题目给出只有两个时,这种方法是不是就满足不了呀!因此,我们还需额外的进行判断这种情况。

对于它的处理办法,有两种:

💨 ①我们可以把条件写死,就是如果只有两个元素,且元素不同,那么结果为2,直接返回即可;

💨 ②为了运用上述的那种规则体系,我们可以在第一个元素之前假入一个元素即可使用上述公式的运行,那这个数字应该是什么呢?

  1. 之前我们在讨论情况一时得出过相同数字连续的时候, prediff = 0 ,laterdiff < 0 或者 >0 也记为临界
  2. 那么为了规则统一,针对序列[1,2],可以假设为[1,1,2],这样它就有坡度了即preDiff = 0,如图:

 针对以上情形,res初始为1(默认最右面有一个峰值),此时laterdiff > 0 && prediff <= 0,那么res++(计算了左面的峰值),最后得到的 res=2(峰值个数为2即摆动序列长度为2)


3、 单调坡中有平坡

在版本一中,我们忽略了一种情况,即 如果在一个单调坡度上有平坡,例如[1,2,2,2,3,4],如图:

 

我们先看一下这种情况下摆动的最长序列是多少,通过观察,我们发现本题的最长序列为2,因为 单调中的平坡不能算临界。

因此,我们只需要在 这个坡度 摆动变化的时候,更新prediff就行,这样prediff在 单调区间有平坡的时候 就不会发生变化,造成我们的误判。

代码展示:

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if (nums.size() <= 1) 
            return nums.size();
            
        int Count = 1;  // 记录峰值个数,序列默认序列最右边有一个峰值
        int prediff = 0; // 当前一对差值
        int laterdiff = 0; // 前一对差值
        

        for (int i = 0; i < nums.size() - 1; i++) {
            prediff = nums[i + 1] - nums[i];
            // 出现峰值
            if ((laterdiff <= 0 && prediff > 0) || (laterdiff >= 0 && prediff < 0)) {
                Count++;
                laterdiff = prediff; // 注意这里,只在摆动变化的时候更新prediff 
            }
        }
        return Count;
    }
};

性能分析:

  • 时间复杂度:O(n)。其中n 是序列的长度。我们只需要遍历该序列一次。
  • 空间复杂度:O(1)。我们只需要常数空间来存放若干变量。

(二)动态规划

根据动态规划的思想,每当我们选择一个元素作为摆动序列的一部分时,这个元素要么是上升的,要么是下降的,这取决于前一个元素的大小。每一个数值处于两种状态,如 【1 2 3】,此时2的prediff 处于正值状态,2的 laterdiff 处于负值状态,那么我们需要有两个变量来存储。

由于题目的要求是求出最长的摆动序列,那么动态规划的状态转折表达式中设置两个状态数组:

  • up[i] :表示以前 i 个元素中的某一个为结尾的最长的「上升摆动序列」的长度。
  • down[i] :表示以前 i 个元素中的某一个为结尾的最长的「下降摆动序列」的长度。

那么流思维导图:

 在这一思路上,我们还可以对其进行改进。既然已知每一个数处于的状态,如位置i的数,我们已知位置i前面所有的状态,,那么我们可以一次性遍历即可::

在上述优化下,我们还可以将空间缩小,因为我们发现每次参加运算的变量只有位置i和位置i - 1两个数,因为,可以直接用两个变量代替即可。 

代码展示:

 

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int n = nums.size();
        if(n == 0)
            return 0;
        int up = 1;
        int down = 1;
        for(int i = 1;i < n;++i){
            if(nums[i] - nums[i - 1] > 0)
                up =down + 1;
            else if(nums[i] - nums[i - 1] < 0)
                down =up + 1;
        }
        return max(down,up);
    }
};

性能分析:

  • 时间复杂度:O(n)。其中 n 是序列的长度。我们只需要遍历该序列一次。
  • 空间复杂度:O(1)。我们只需要常数空间来存放若干变量。


到此,关于本题的讲解便到此结束了。最后,希望本题的讲解对大家有所帮助!!

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

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

相关文章

跑在笔记本里的大语言模型 - GPT4All

何为GPT4All GPT4All 官网给自己的定义是&#xff1a;一款免费使用、本地运行、隐私感知的聊天机器人&#xff0c;无需GPU或互联网。 从官网可以得知其主要特点是&#xff1a; 本地运行&#xff08;可包装成自主知识产权&#x1f436;&#xff09;无需GPU&#xff08;穷人适配…

sort、uniq、tr、cut的使用

管理文件内容的使用 一、sort命令二、uniq命令三、tr命令四、cut命令 一、sort命令 sort命令是以行为单位对文件内容进行排序&#xff0c;也可以根据不同的数据类型来排序&#xff0c;比较原则是从首字符向后&#xff0c;依次按ASCII码进行比较&#xff0c;最后将他们按升序输…

Linux:rpm查询安装 yum安装

环境&#xff1a; 需要插入安装镜像 镜像内有所需的安装库 我这里使用的虚拟机直接连接光盘 连接的光盘挂载在/dev/cdrom 由于我们无法直接进入&#xff0c;所以选择把/dev/cdrom挂载到别的地方即可 mount /dev/cdrom /123 将/dev/cdrom 挂载到 /123 目录下 Packages下就是…

C++笔记—— 第十七篇 智能指针 C++11来了(下)

目录 1. 为什么需要智能指针 2. 内存泄漏 2.1 什么是内存泄漏&#xff0c;内存泄漏的危害 2.2 内存泄漏分类 2.3如何避免内存泄漏 3.智能指针的使用及原理 3.1 RAII 3.2 智能指针的原理 3.3 std::auto_ptr 3.4 std::unique_ptr 3.5 std::shared_ptr shared_ptr的线…

JVM性能调优

一、JVM内存模型及垃圾收集算法 1.根据Java虚拟机规范&#xff0c;JVM将内存划分为&#xff1a; New&#xff08;年轻代&#xff09; Tenured&#xff08;年老代&#xff09; 永久代&#xff08;Perm&#xff09; 其中New和Tenured属于堆内存&#xff0c;堆内存会从JVM启动参…

【牛客刷题专栏】0x28:JZ30 包含min函数的栈(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转)&#xff0c;它登陆后会保存刷题记录进度&#xff0c;重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏&#xff1a;个人CSDN牛客刷题专栏。 题目来自&#xff1a;牛客/题库 / 在线编程 / 剑指offer&#xff1a; 目录 前言问…

【神经网络】tensorflow实验9--分类问题

1. 实验目的 ①掌握逻辑回归的基本原理&#xff0c;实现分类器&#xff0c;完成多分类任务&#xff1b; ②掌握逻辑回归中的平方损失函数、交叉熵损失函数以及平均交叉熵损失函数。 2. 实验内容 ①能够使用TensorFlow计算Sigmoid函数、准确率、交叉熵损失函数等&#xff0c…

(浙大陈越版)数据结构 第二章 线性结构 2.4 多项式的加法和乘法运算实现

目录 2.4.1多项式的加法运算实现 如何设计一个函数分别求两个一元多项式的和&#xff1f; 算法思路&#xff1a;两个指针p1&#xff0c;p2分别指向两个多项式的第一个结点&#xff08;最高项&#xff09;并循环 循环&#xff1a; 2.4.2 多项式的乘积 1.多项式的表示 2.程…

IPsec VPN IKE方式协商密钥

实验拓扑 要求pc1与pc2两个网络访问走ipsec隧道互访。 前言&#xff1a; ipsecs 隧道两端的acl规则定义的协议类型要一致&#xff0c;如果一端是ip协议&#xff0c;另一端也必须是ip协议 配置acl的原因是&#xff1a;1&#xff0c;通过acl&#xff08;permit&#xff09;指定需…

Metalama released Crack

Metalama released Crack Metalama是一个面向C#的元编程框架。它可以帮助您提高代码质量和生产力。使用Metalama&#xff0c;您可以通过在编译过程中动态生成样板文件来减少样板文件。您的源代码仍然非常清晰。根据体系结构、模式和约定实时验证代码。无需等待代码评审。通过定…

React从入门到实战 - React初体验

文章目录 特点相关JS库步骤准备步骤操作步骤 关于虚拟DOMJSX语法规则函数式组件常见的几种错误正确写法 类式组件 特点 采用组件化模式&#xff0c;声明式编程&#xff0c;提高开发效率及组件复用率在React Native 中使用React语法进行移动端开发使用虚拟DOM优秀的Diffing算法…

Java8 map.getOrDefault()你真的了解吗

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 map.getOrDefault()方法 在Java编程中&#xff0c;Map是一种非常常用的数据结构。Map通常用于存储键值对&#xff0c;其中每个键映射到一个值。当我们尝试访问一个不…

文件一直处于修改状态 git checkout 无法还原的问题解决方法

问题描述 最近在 RT-Thread 时&#xff0c;使用 Git 回退版本验证问题&#xff0c;后来 git pull 拉取最新代码后&#xff0c;发现里面有几个文件&#xff0c;一直为【修改】状态&#xff0c;并且无法还原&#xff0c;git checkout xxx git reset --hard 都用了&#xff0c;依旧…

基于AT89C51单片机的温度控制系统报警器

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87771724?spm1001.2014.3001.5503 源码获取 单片机读取温度传感器当前的温度值并在LCD液晶显示屏上的第一行显示当前的温度值&#xff0c;单片机读取按键状态并通过…

Flask框架之异常处理、请求钩子、上下文的使用

Flask框架之异常处理、请求钩子、上下文的使用 异常处理捕获指定异常状态码捕获指定异常类型抛出HTTP Exception 请求钩子概述基本使用 请求上下文概述应用上下文current_app对象g对象 请求上下文request对象session对象 异常处理 捕获指定异常状态码 可以使用app.errorhandle…

Github的使用

1.基本概念&#xff1a; 仓库&#xff08;Repository&#xff09;:仓库用来存放项目代码&#xff0c;每个项目对应一个仓库&#xff0c;多个开源项目则有多个仓库 收藏&#xff08;Star&#xff09;:收藏项目&#xff0c;方便下次查看 复制克隆项目:&#xff08;Fork&#x…

JVM类加载、类变量初始化顺序

题目 先来看一个题目&#xff0c;以下程序的输出结果是什么 运行结果 关于类加载机制 关于JVM的类加载 《深入理解Java虚拟机》中关于类加载是这样说的&#xff1a; Java虚拟机把描述类的数据从Class文件加载到内存&#xff0c;并对数据进行校验、转换解析和初始化&#x…

【SLAM学习】基于Pangolin绘制运动轨迹

Pangolin是一个轻量级的跨平台视图控制库&#xff0c;主要用于可视化、交互和调试三维数据。该库提供了一系列图形界面工具&#xff0c;包括窗口、OpenGL渲染器、3D相机、图像显示等&#xff0c;可以方便地进行三维数据可视化和交互。 Pangolin库的主要特点如下&#xff1a; 轻…

SpringBoot的多配置文件

文章目录 1.配置文件的命名规则2.配置文件选择3.配置文件的优先级 1.配置文件的命名规则 配置文件一般要求以application开头&#xff0c;可以是.yml结尾的文件&#xff0c;也可以是.properties结尾的文件。 2.配置文件选择 当有多个配置文件&#xff0c;需要指定其中一个生…

关于C语言取余运算的那些大坑

0.前言 您好&#xff0c;这里是limou3434的一篇个人博文&#xff0c;感兴趣的话您也可以看看我的其他文章&#xff0c;本次我想给您带来的是关于C语言操作符‘%’的一些奇怪现象以及背后的原理解释&#xff0c;本章用了一点点python语法&#xff08;比如在python中“//”是整除…