【力扣题解】376. 摆动序列 -(动态规划)

Problem: 376. 摆动序列

1. 状态表示

这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义一个状态表示:

dp[i]表示:以 i 位置为结尾的最长摆动序列的长度

但是,问题来了,如果状态表示这样定义的话,以 i 位置为结尾的最⻓摆动序列的⻓度我们没法从之前的状态推导出来。因为我们不知道前⼀个最长摆动序列的结尾处是递增的,还是递减的。因此,我们需要状态表⽰能表⽰多⼀点的信息:要能让我们知道这⼀个最⻓摆动序列的结尾是递增的还是递减的。

解决办法:搞两个 dp

f[i] 表⽰:以 i 位置元素为结尾的所有的子序列中,最后⼀个位置呈现「上升趋势」的最长摆动序列的长度;

g[i] 表⽰:以 i 位置元素为结尾的所有的子序列中,最后⼀个位置呈现「下降趋势」的最长摆动序列的长度。

2. 状态转移方程

由于子序列的构成比较特殊, i 位置为结尾的子序列,前一个位置可以是 [0, i - 1] 的任意位置,因此设 j[0, i - 1] 区间内的某一个位置。

对于 f[i] ,我们可以根据「子序列的构成方式」,进⾏分类讨论:
    1. 子序列长度为 1: 只能自己和自己玩了,此时 f[i] = 1; 
    2. 子序列长度大于 1:因为结尾要呈现上升趋势,因此需要 nums[j] < nums[i]。在满足这个条件下 j 结尾需要呈现下降状态,最长摆动序列就是 g[j] + 1
因此,我们要找出所有满足条件下的最大的 g[j] + 1。
综上所述,f[i] = max(g[j] + 1, f[i])


对于 g[i] ,我们可以根据「子序列的构成方式」,进⾏分类讨论:
    1. 子序列长度为 1: 只能自己和自己玩了,此时 g[i] = 1; 
    2. 子序列长度大于 1:因为结尾要呈现下降趋势,因此需要 nums[j] > nums[i]。在满足这个条件下 j 结尾需要呈现上升状态,最长摆动序列就是 f[j] + 1
因此,我们要找出所有满足条件下的最大的 f[j] + 1。
综上所述,g[i] = max(f[j] + 1, g[i])

3. 初始化

所有的元素「单独」都能构成⼀个摆动序列,因此可以将 dp 表内所有元素初始化为 1 。

4. 填表顺序

毫无疑问「从左往右」

5. 返回结果

应该返回「两个dp表⾥⾯的最⼤值」,我们可以在填表的时候,顺便更新⼀个「最⼤值」。

6. 复杂度分析

时间复杂度上,嵌套了两层循环,所以时间复杂度是 O ( n 2 ) O(n^2) O(n2)
空间复杂度上,用了两个额外的 dp 表,所以空间复杂度是 O ( n ) O(n) O(n)

7. Code

7.1 C++代码

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        // 1. 创建 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回结果

        // f[i]: 表示以 i 位置为结尾的所有子序列中,最后一个位置呈现 "上升" 趋势的最长摆动序列的长度
        // g[i]: 表示以 i 位置为结尾的所有子序列中,最后一个位置呈现 "下降" 趋势的最长摆动序列的长度
        int n = nums.size();
        vector<int> f(n, 1), g(n, 1);
        int ret = 1;
        for(int i = 1; i < n; i++) {
            for(int j = 0; j < i; j++) {
                if(nums[j] < nums[i]) f[i] = max(g[j] + 1, f[i]);
                else if(nums[j] > nums[i]) g[i] = max(f[j] + 1, g[i]);
            }
            ret = max(ret, max(f[i], g[i]));
        }
        return ret;
    }
};

7.2 java代码

class Solution {
    public int wiggleMaxLength(int[] nums) {
        // 1. 创建 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回值

        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];
        for (int i = 0; i < n; i++)
            g[i] = f[i] = 1;

        int ret = 1;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i])
                    f[i] = Math.max(g[j] + 1, f[i]);
                else if (nums[j] > nums[i])
                    g[i] = Math.max(f[j] + 1, g[i]);
            }
            ret = Math.max(ret, Math.max(f[i], g[i]));
        }
        return ret;
    }
}
  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自然语言编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值