leetCode 376.摆动序列 贪心算法

这篇具有很好参考价值的文章主要介绍了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 此时就有波动就需要统计

摆动序列贪心算法,贪心算法,leetCode,leetcode,算法,贪心算法,摆动序列

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

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

摆动序列贪心算法,贪心算法,leetCode,leetcode,算法,贪心算法,摆动序列

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

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

摆动序列贪心算法,贪心算法,leetCode,leetcode,算法,贪心算法,摆动序列

在图中,当 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。而这正是上文讨论的情况一,那么也可以记为一个波谷

摆动序列贪心算法,贪心算法,leetCode,leetcode,算法,贪心算法,摆动序列

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

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

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

摆动序列贪心算法,贪心算法,leetCode,leetcode,算法,贪心算法,摆动序列

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

摆动序列贪心算法,贪心算法,leetCode,leetcode,算法,贪心算法,摆动序列

什么时候该更新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

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

摆动序列贪心算法,贪心算法,leetCode,leetcode,算法,贪心算法,摆动序列文章来源地址https://www.toymoban.com/news/detail-729642.html

到了这里,关于leetCode 376.摆动序列 贪心算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法 贪心1 || 455.分发饼干 376. 摆动序列 53. 最大子数组和

    什么是贪心:贪心的本质是选择每一阶段的局部最优,从而达到全局最优。 但是贪心没有套路,做题的时候,只要想清楚 局部最优 是什么,如果推导出全局最优,其实就够了。 很容易想到,把孩子的胃口和饼干大小都排序,都从最小值开始遍历。如果最小的饼干无法满足最

    2023年04月16日
    浏览(36)
  • LeetCode刷题笔记【23】:贪心算法专题-1(分发饼干、摆动序列、最大子序和)

    贪心的本质是选择每一阶段的局部最优,从而达到全局最优。 例如,有一堆钞票,你可以拿走十张,如果想达到最大的金额,你要怎么拿? 指定每次拿最大的,最终结果就是拿走最大数额的钱。 每次拿最大的就是局部最优,最后拿走最大数额的钱就是推出全局最优。 感觉像

    2024年02月09日
    浏览(42)
  • Day31 贪心算法 part01 理论基础 455.分发饼干 376.摆动序列 53.最大子序和

    什么是贪心 贪心的本质是选择每一阶段的局部最优,从而达到全局最优 。 这么说有点抽象,来举一个例子: 例如,有一堆钞票,你可以拿走十张,如果想达到最大的金额,你要怎么拿? 指定每次拿最大的,最终结果就是拿走最大数额的钱。 每次拿最大的就是局部最优,最

    2024年01月19日
    浏览(35)
  • 算法题:摆动序列(贪心算法解决序列问题)

    这道题是一道贪心算法题,如果前两个数是递增,则后面要递减,如果不符合则往后遍历,直到找到符合的。(完整题目附在了最后) 代码如下: 完整题目: 376. 摆动序列 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为  摆动序列 。 第一个差(如果

    2024年02月07日
    浏览(37)
  • 摆动序列【贪心算法】

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

    2024年02月11日
    浏览(32)
  • 摆动序列——力扣376

    题目描述 贪心

    2024年02月13日
    浏览(23)
  • 代码随想录27|455.分发饼干,376. 摆动序列,53. 最大子序和

    链接地址 链接地址 链接地址

    2024年02月11日
    浏览(29)
  • 【leetcode】贪心算法介绍

    详细且全面地分析贪心算法常用的解题套路、数据结构和代码逻辑如下: 找最值型: 每一步选择都是局部最优解,最后得到的结果就是全局最优解。 常用于找零钱问题、区间覆盖问题等。 一般情况下,可以通过排序将数据进行处理,然后逐步选择最优解。 区间问题: 将问

    2024年02月21日
    浏览(27)
  • 【贪心算法】leetcode刷题

    贪心算法无固定套路。 核心思想:先找局部最优,再扩展到全局最优。 两种思路: 1、从大到小。局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。 先遍历的胃口,在遍历的饼干 2、从小到大。 小饼干先喂饱小胃口 。两个

    2024年02月14日
    浏览(33)
  • leetcode系列贪心算法汇总

    11 盛水最多的容器 题目:给一个一维数组,大概的意思就是下标代表水槽的宽度,数组的值代表这个位置水槽的高度,求盛水最多的容量。 解析:肯定得有个临时变量来存最大值,且不断进行比较来更新最大值,然后分别从两边开始使用双指针进行遍历,tmp := (right - left)

    2024年02月07日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包