LeetCode每日一题--376. 摆动序列(贪心 动态规划)

题目:跳转至 376. 摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
示例 1:
输入: [1,7,4,9,2,5]
输出: 6
解释: 整个序列均为摆动序列。
示例 2:
输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。
示例 3:
输入: [1,2,3,4,5,6,7,8,9]
输出: 2
进阶:
你能否用 O(n) 时间复杂度完成此题?

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {

    }
};

思路:
先随便发散一下思维,首先,摆动序列定义中连续数字差严格在正负数间交替,那如果出现0必然会分为两段,在此基础上要保证两两差值之间是正负交替的话,其乘积必大于0。分割两段表述有点问题,题目最后一段说明是可以删去部分原始数组的元素。(这段可以略过没用)
或者如何保证两两差值交替正负呢?不会出现递增或者递减就行。
如果出现递增序列,如[3,1,2,3,4,3]中[1,2,3,4]很明显递增,去除中间的[2,3]那[3,1,4,3]就成了一个摆动序列,因为递增的起点必小于前一个元素,顶点必大于后一个元素。
真的暴力破解,刚开始没有考虑到[1,1,1,1,1,1]和[1,1,1,7,3,5,1]这种,提交连连报错。终于搞定了,成功之后赶忙看题解,沉默是今晚的康桥,62行那么长情何以堪。

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int len=nums.size();
        if(len==0)
            return 0;
        int maxn=INT_MIN;int minn=INT_MAX;
        for(auto x:nums){
            maxn=max(maxn,x);
            minn=min(minn,x);
        }
        if(maxn==minn)
            return 1;
        vector<pair<int,int>> saveIncrease;  //pair<起点下标,长度>
        vector<pair<int,int>> saveDecrease;
        int lastDiff=nums[1]-nums[0];
        int i=1;
        while(i<len){
            //如果连续数字相同那只要保留一位就可以,其余都去掉
            int count=0;
            while(i<len && nums[i-1]==nums[i]){
                ++count;
                ++i;
            }
            if(count>=1 && i<len && nums[i-1]<nums[i])
                saveIncrease.emplace_back(make_pair(i-count-1,count));
            else if(count>=1 && i<len && nums[i-1]>nums[i])
                saveDecrease.emplace_back(make_pair(i-count-1,count));
            //如果是递增(包含相等)过程中,需要保留头和尾两处值
            count=0;
            while(i<len && nums[i-1]<=nums[i]){
                ++count;
                ++i;
            }
            if(count>1)
                saveIncrease.emplace_back(make_pair(i-count,count-1));
            //如果是递减(包含相等)过程中,同样需要保留头和尾两处值
            int count1=0;
            while(i<len && nums[i-1]>=nums[i]){
                ++count1;
                ++i;
            }
            if(count1>1)
                saveDecrease.emplace_back(make_pair(i-count1,count1-1));
            if(count==0 && count1==0)
                ++i;
        }
        for(int i=0;i<saveIncrease.size();++i){
            for(int j=saveIncrease[i].first;j<saveIncrease[i].first+saveIncrease[i].second;++j)
                nums[j]=INT_MAX;
        }
        for(int i=0;i<saveDecrease.size();++i){
            for(int j=saveDecrease[i].first;j<saveDecrease[i].first+saveDecrease[i].second;++j)
                nums[j]=INT_MAX;
        }
        int res=0;
        for(auto x:nums){
            if(x<INT_MAX)
                ++res;
        }
        return res;
    }
};

题解两种方法:动态规划、贪心。个人觉得贪心好理解一点,和原本思路也相近,题解是只统计了数量。

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int n = nums.size();
        if (n < 2) {
            return n;
        }
        int prevdiff = nums[1] - nums[0];
        int ret = prevdiff != 0 ? 2 : 1;  //把开头就连续的情况考虑到
        for (int i = 2; i < n; i++) {
            int diff = nums[i] - nums[i - 1];
            if ((diff > 0 && prevdiff <= 0) || (diff < 0 && prevdiff >= 0)) {  //峰谷必须交替且差值不能为0即连续数相等
                ret++;
                prevdiff = diff;
            }
        }
        return ret;
    }
};

动态规划想想硬着头皮也看一下吧,
up[i](down[i])分别存储以前i个元素中的某一个为结尾的最长的上升(下降)摆动序列的长度。
以up[i]为例:
nums[i]<=nums[i-1]时,这是一个下降(或相等)过程,因为没有上升必然只能up[i]=up[i-1];
nums[i]>nums[i-1]时,这是一个上升过程,先最长下降摆动序列加一(down[i-1]+1),再放入最长上升摆动序列与原本值up[i-1]比较取最大,up[i]=max(up[i-1],down[i-1]+1);
down[i]同理。
最终的答案为up[n-1]和down[n−1]中的较大值,其中n是序列的长度。

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int n = nums.size();
        if (n < 2) {
            return n;
        }
        vector<int> up(n), down(n);
        up[0] = down[0] = 1;
        for (int i = 1; i < n; i++) {
            if (nums[i] > nums[i - 1]) {
                up[i] = max(up[i - 1], down[i - 1] + 1);
                down[i] = down[i - 1];
            } else if (nums[i] < nums[i - 1]) {
                up[i] = up[i - 1];
                down[i] = max(up[i - 1] + 1, down[i - 1]);
            } else {
                up[i] = up[i - 1];
                down[i] = down[i - 1];
            }
        }
        return max(up[n - 1], down[n - 1]);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值