代码随想录Day33

今天继续学习贪心算法相关,需要着重理解思路。

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) 。

思路:

1. 对于初次系统接触贪心算法的我来说这道题从理解思路上已经开始有些许难度了。首先弄清楚摆动的条件,因此我们需要两个整型变量prediff和curdiff分别来存储当前位置数字与前一个数字之差以及后一个数字与当前数字之差进行比较。

2.然后根据题目里的提示,我们没必要非得在原数组中进行元素的删除,我们只需要定义一个结果变量,在遍历的过程中发现一个摆动就直接结果变量增加就可以了。

3.然后再来想特殊情况:(1)出现平坡;(2)数组首部和尾部;(3)在单调之上平坡。

为了解决问题(1),在判断摆动条件时我们需要加上等于符号,但一定要注意,要么都在判断prediff的时候取等,要么都在判断curdiff的时候取等

为了解决问题(2),最直截了当的就是额外在开头判断如果整个数组只有两个数字且大小不等那么直接返回。不过为了让除了只有一个数字之外的其他都符合我们的循环情况,我们可以假设数组最开头还有一个元素并且和第一个元素大小相等,这样在计算数组首部的时候最初的prediff = 0,curdiff > 0或者<0,满足我们解决问题(1)时加入的等于逻辑,结果变量依然会记录。与此同时我们让结果变量初值为1,即默认最后一个元素必定有一个峰值,这样就解决了首部和尾部的问题。

为了解决问题(3),首先在前两个问题较容易想到的情况下第三个问题往往是在提交出错后才会发现,原本我们是每遍历一次,prediff就会更新。那么当出现单调平坡时,在平坡结束之时仍会记录一次结果导致出错(例如1,2,2,2,3,4,在到达最后一个2时prediff = 0, curdiff = 1,满足我们的判断条件)。那么为了防止这种情况,我们选择在出现摆动时才更新prediff

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if(nums.size() == 1) return 1;
        int prediff = 0;//记录nums[i] - nums[i - 1]
        int curdiff = 0;//记录nums[i + 1] - nums[i]

        int result = 1;//默认最后一定有一个摆动,防止数组中只有两个元素无法计算curdiff和prediff
        for(int i = 0; i < nums.size() - 1; i++){
            curdiff = nums[i + 1] - nums[i];
            if((prediff >= 0 && curdiff < 0) || (prediff <= 0 && curdiff > 0)){
                //出现摆动,记录结果,更新prediff
                result++;
                prediff = curdiff;
                //注意,只有当出现摆动时才更新prediff,如果每一次遍历都更新一次prediff,当出现单调平坡的状况时会多计数
            }
        }
        return result;
    }
};

启发:

1.本题通过“删除”单调坡上的元素,只剩下峰值以做到局部最优,最终保证整体均为峰值的摆动序列。对于平坡和数组首尾元素的处理的思路需要进一步加深理解。

53.最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

思路:

1.本题既然需要返回最大和,那么一定要有一个变量来存储连续和。假设从-2开始,此时-2 + 1后还是-1,我们想一个问题:如果当前连续和已经是一个负数,那么不管后面的数正负,我们加上后面的数就一定会导致后面的数变小,那么为什么不直接从后面的数开始记录连续和呢

这就是本题的局部最优:当连续和为负数时我们直接重置连续和,从下一个元素开始重新记录连续和。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int count = 0;
        int result = INT_MIN;

        for(int i = 0; i < nums.size(); i++){
            count += nums[i];
            result = max(result, count);
            //如果连续和已经小于0,那么重置
            if(count <= 0){
                count = 0;
            }
        }
        return result;
    }
};

启发:

1.本题相对于上一题而言较为简单,但贪心的思路仍需进一步理解。对于我个人来说一开始想到直接抛弃连续和小于0这一点还是有些许困难。

但提出思路中的那一个问题后就会发现当连续和小于0的时候确实没必要再继续用当前的连续和了,不如直接从下一个元素重新开始记录连续和。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值