leetCode 376.摆动序列 贪心算法

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

  • 相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。

示例 1:

输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。

示例 2:

输入:nums = [1,17,5,10,13,15,10,5,16,8]
输出:7
解释:这个序列包含几个长度为 7 摆动序列。
其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。

示例 3:

输入:nums = [1,2,3,4,5,6,7,8,9]
输出:2

>>思路和分析

思路1:贪心思路

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

试试贪心:局部最优推出全局最优,并举不出反例!!!

实际操作上,可以不用做删除操作,由于题目要求的是最长摆动子序列的长度,所以只需要统计数组的局部峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)这也就是贪心所贪的地方。

(1)如何表示一个波动呢?

curdiff = nums[i+1] - nums[i];
prediff = nums[i] - nums[i-1];

如果prediff < 0 && curdiff > 0 或者 prediff > 0 && curdiff < 0 此时就有波动就需要统计

(2)如果出现平坡又该如何解决呢? 我们继续往下看~

平坡有两种:一个是 上下中间有平坡,一个是 单调中间有平坡 

① 情况一:上下坡中有平坡

例如 [1,2,2,2,1],它的摆动序列长度是3,也就是我们在删除的时候要不删除左面的三个2,要不就删除右面的三个2

在图中,当 i 指向第一个2的时候,prediff > 0 && curdiff = 0,当 i 指向最后一个2的时候,prediff = 0 && curdiff < 0

若采用删除左面三个2的规则,那么 当 i 指向第一个2的时候,prediff = 0 && curdiff < 0,也要记录一个峰值。这是由于它是把之前相同的元素都删除留下的峰值

所以这里记录峰值的条件可以允许prediff = 0,也就是:prediff <= 0 && curdiff > 0 或者 prediff >= 0 && curdiff < 0 。也就是说相同数字连续的时候,prediff = 0,curdiff < 0 或者 >0 也就为波谷

② 情况二:数组首尾两端

问题思考(O_O)? 统计峰值时,数组的最左面和最右面如何统计呢?

例子:序列[2,5],摆动序列为2。

上文提到 prediff = nums[i] - nums[i-1] 和 curdiff = nums[i+1] - nums[i] 的时候,可知至少需要三个数字才能计算。因其靠统计差值来计算峰值个数就需要考虑数组最左面和最右面的特殊情况。而此时序列数组只有两个数字,如何将我们的判断规则结合在一起呢?

不妨假设数组前面还有一个数字,也就是将序列[2,5],假设为[2,2,5],此时这就有了坡度prediff = 0。而这正是上文讨论的情况一,那么也可以记为一个波谷

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

所以说可以初始化 prediff = 0,result = 1

③ 情况三:单调坡中有平坡

上图中,可以计算最长的摆动的序列的长度为3,但其实结果应该为2。这是因为上图是不加限制的实时更新prediff,这会导致 单调中的平坡被算为峰值。

什么时候该更新prediff呢?只需要在这个坡度摆动变化的时候,更新prediff就行,这样prediff在单调区间有平坡的时候,就不会发生变化,也就不会产生误判!

>>分析上面两张图和问题思考(O_O)?

问题出在prediff 是一直跟着curdiff去更新的,其实prediff是没有必要去跟着curdiff去实时变换的,prediff只需要统计坡度有变化的时候,记录一下这个坡度的原始方向。例如说在第一个 2 的位置,prediff是有变化的,因为在 1 那里默认它有个平坡(情况二),然后在有变化的时候,prediff就记录一下这个坡度的初始值,也就是初始的坡度的方向。后面这个prediff就没有必要去变化了。那它什么时候去变化呢?除非这个坡度的方向改变了,也就是说遇到了一个摆动,之后prediff就可以记录一下下一个坡的坡度方向。那这样,prediff只记录这个坡度变化的时候的初始坡度,这样有什么好处呢?就是遇到平坡的时候prediff是不会去改变的。那这样在这个代码逻辑中,也不会去记录第三个2认为出现了一个摆动(因为第三个2那里如果prediff是一直实时更新的,就出现prediff = 0,curdiff > 0,这种情况其实是情况二的场景,会被认为是一个摆动)。所以,解决方案就是:prediff只记录当摆动出现的时候,下一个坡的初始坡度,然后prediff就不用去改变了,直到遇到下一个摆动的时候,又改变坡的方向了,prediff可以再去改变,这样就可以绕过这种平坡的情况。体现在代码里应该怎么改呢?可以将prediff=curdiff放在if里面。当出现摆动的时候,再去更新prediff,这样就可以绕过平坡的这种情况。

class Solution {
public:int wiggleMaxLength(vector<int>& nums) {if(nums.size() <= 1) return nums.size();int prediff = 0; // 前一对差值int curdiff = 0; // 当前一对差值int result = 1; // 记录峰值个数,序列默认序列最右边有一个峰值for(int i=0;i<nums.size()-1;i++) {curdiff = nums[i+1] - nums[i];// prediff=curdiff放在if里面,为的是处理单调有平坡的这种情况if((prediff >=0 && curdiff<0) || (prediff <=0 && curdiff>0)) { // 出现峰值result++;prediff = curdiff; // 注意这里,只在摆动变化的时候更新prediff}}return result;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

prediff = curdiff 放在 if 里面,为的是处理单调有平坡的这种情况。若放在 if 外面,则会出现上文所述的误判!!!

参考和推荐文章、视频

代码随想录 (programmercarl.com)

贪心算法,寻找摆动有细节!| LeetCode:376.摆动序列_哔哩哔哩_bilibili

来自代码随想录的课堂截图:

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像拼接后丢失数据,转tiff报错rasterfile failed: an unknown

图像拼接后丢失数据 不仅是数据丢失了&#xff0c;还有个未知原因报错 部分数据存在值不存在的情况 原因 处理遥感数据很容易&#xff0c;磁盘爆满了 解决方案 清理一些无用数据&#xff0c;准备买个2T的外接硬盘用着了。 然后重新做处理

[Linux] 4.常用初级指令

pwd&#xff1a;显示当前文件路径 ls:列出当前文件夹下有哪些文件 mkdir空格文件名&#xff1a;创建一个新的文件夹 cd空格文件夹名&#xff1a;进入文件夹 cd..&#xff1a;退到上一层文件夹 ls -a&#xff1a;把所有文件夹列出来 .代表当前文件夹 ..代表上层文件夹 用…

【Vue】Vuex详解,一文读懂并使用Vuex

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《ELement》。&#x1f3af;&#x1f3af; &#x1…

【15】c++设计模式——>抽象工厂模式

在海贼世界中&#xff0c;位于水之都的弗兰奇一家是由铁人弗兰奇所领导的以拆船为职业的家族&#xff0c;当然了他们的逆向工程做的也很好&#xff0c;会拆船必然会造船。船是海贼们出海所必备的海上交通工具&#xff0c;它由很多的零件组成&#xff0c;从宏观上看它有这么几个…

智慧财务管家,记录分析收支明细,轻松掌握财务情况并随时打印保存!

在日常的财务管理中&#xff0c;准确记录和分析收支明细是掌握财务情况、制定科学预算和实现财务目标的重要一环。然而&#xff0c;繁琐的手动记录和分析过程常常让我们感到头痛。现在&#xff0c;让我们向您推荐一款智慧财务管家&#xff0c;帮助您轻松记录和分析收支明细&…

仿函数的学习

仿函数 也叫 函数对象 仿函数是什么东西&#xff1f; 当你第一眼看到下面的代码的时候&#xff0c;你会觉得它是一个函数的调用&#xff1a; bool result less(a, b);但是我如果告诉你&#xff0c;less 是一个我自定义的一个类的对象呢&#xff1f; class Less { public:bo…

Interference Signal Recognition Based on Multi-Modal Deep Learning

系统结构 基于决策的融合实际上是用损失函数监督融合模型 其中 N N N是训练样本的数量 体会 作者未解释公式4的 t i t_i ti​的含义且不公布代码

elment以及elementPlus选中组件出现黑框问题解决!!

目录 问题&#xff1a; 图示&#xff1a; 解决方案&#xff1a; 问题&#xff1a; 使用elementPlus的按钮组件&#xff0c;点击按钮后会出现黑框&#xff0c;除非点击其他地方才能取消掉&#xff08;之前使用elment-ui其它组件时也出现过&#xff09; 图示&#xff1a; 解决方案…

Day-07 修改 Nginx 配置文件

至此&#xff1a; 简单的 Docker 安装 Nginx并启动算是成功了! ps: 如何修改 Nginx的配置、更改nginx 的资源文件&#xff1f; eg&#xff1a; 1、可以将容器中的目录和本机目录做映射。 2、达到修改本机目录文件就影响到容器中的文件。 1.本机创建实例文件夹 新建目录&#x…

【机器学习-黑马程序员】人工智能、机器学习概述

文章目录 前言一、人工智能概述二、什么是机器学习二、机器学习算法分类三、机器学习开发流程 前言 本专栏文章为观看黑马程序员《python机器学习》所做笔记&#xff0c;课程地址在这。如有侵权&#xff0c;立即删除。 一、人工智能概述 机器学习和人工智能、深度学习的关系 机…

即时通讯软件

通信协议 发送消息可以是个struct 客户端分两个线程&#xff1a;读取服务器&#xff0c;给服务器发&#xff08;否则会导致阻塞&#xff09; read和write的第二个参数类型是&#xff1a;void *buf——————不仅仅是一个字符串&#xff0c;也可以是一个结构体等等&#xf…

获取沪深300的所有个股列表

脚本&#xff1a; import requests from bs4 import BeautifulSoupurl "https://q.stock.sohu.com/cn/bk_4444.shtml" response requests.get(url) soup BeautifulSoup(response.text, "html.parser")# 找到包含class为e1的元素 elements soup.find_a…

NodeMCU ESP8266硬件开发板的熟悉

文章目录 硬件开发环境的熟悉基础介绍什么是 ESP8266 NodeMCU&#xff1f;NodeMCU芯片ESP12-E 模组开发板 ESP8266 版本引脚图Power GND I2CGPIOADCUARTSPIPWMControl 总结 硬件开发环境的熟悉 基础介绍 什么是 ESP8266 NodeMCU&#xff1f; ESP8266是乐鑫开发的一款低成本 …

阿里云服务器搭建网站(图文新手教程)

使用阿里云服务器快速搭建网站教程&#xff0c;先为云服务器安装宝塔面板&#xff0c;然后在宝塔面板上新建站点&#xff0c;阿里云服务器网以搭建WordPress网站博客为例&#xff0c;来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流程&#xff1a; …

TempleteMethod

TempleteMethod 动机 在软件构建过程中&#xff0c;对于某一项任务&#xff0c;它常常有稳定的整体操作结构&#xff0c;但各个子步骤却有很多改变的需求&#xff0c;或者由于固有的原因 &#xff08;比如框架与应用之间的关系&#xff09;而无法和任务的整体结构同时实现。如…

【进程管理】初识进程

一.何为进程 教材一般会给出这样的答案: 运行起来的程序 或者 内存中的程序 这样说太抽象了&#xff0c;那我问程序和进程有什么区别呢&#xff1f;诶&#xff1f;这我知道&#xff0c;书上说&#xff0c;动态的叫进程&#xff0c;静态的叫程序。那么静态和动态又是什么意思…

坦克世界WOT知识图谱三部曲之爬虫篇

文章目录 关于坦克世界1. 爬虫任务2. 获取坦克列表3. 获取坦克具体信息结束语 关于坦克世界 《坦克世界》(World of Tanks, WOT)是我在本科期间玩过的一款战争网游&#xff0c;由Wargaming公司研发。2010年10月30日在俄罗斯首发&#xff0c;2011年4月12日在北美和欧洲推出&…

SQL:增、删、改、查 基本语句 Navicat建库(用法 + 例子)

文章目录 新建数据库新建表 增、删、改、查select 查找insert 添加delete 删除update 修改where 扩展 < > < > ! <> 比较运算符and or 逻辑运算符between...and... 介于..和..之间in 包含like 模糊查询is null 为空的 查询扩展order by 排序limit start coun…

【计算机组成原理】考研真题攻克与重点知识点剖析 - 第 2 篇:数据的表示和运算

前言 本文基础知识部分来自于b站&#xff1a;分享笔记的好人儿的思维导图与王道考研课程&#xff0c;感谢大佬的开源精神&#xff0c;习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析&#xff0c;本人技术…

mysql日期月份相关函数

从给定日期提取最后一天&#xff1a; 要知道2017年12月的最后日期&#xff0c;可以按以下方式执行LAST_DAY()函数&#xff1a;用法:输出&#xff1a; 2017-12-31 从给定的日期时间中提取最后一天&#xff1a; 要使用日期时间格式了解月份的最后日期&#xff0c;可以按以下方式…