LeetCode - 376. 摆动序列(动态规划)

376. 摆动序列

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

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

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

LeetCode - 376. 摆动序列(动态规划)_第1张图片
解题思路: 解子序列的问题,尤其是极值问题,优先应该想到的是用动态规划,Dynamic Programming,这应该是条件反射~,OK,大的思路确定下来了,现在看怎么解。动态规划解题一般有两个难点,一个是如何定义状态变量,以及如何解耦两个状态的转换,即写状态转移方程。那么此题要求的是摆动序列,而不像之前做过的升序序列,只需要一个动态规划即可解题,而这里需要把问题分解成,如何当前求取的是递增,那么它的前一个状态应该是递减,反之亦然,那么问题到此就已经分解了,定义两个变量,up[i],down[i]表示前i个字符串能获取的最大摆动子序列,并且最后一个摆动是递增(递减),动态转移方程为:up[i] = max(up[i], down[j] + 1),down[i] = max(down[i], up[j] + 1)。

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if (nums.empty()) return 0;
        int n = nums.size();
        vector<int> up(n, 1), down(n, 1);
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (nums[i] > nums[j]) up[i] = max(up[i], down[j] + 1);
                if (nums[i] < nums[j]) down[i] = max(down[i], up[j] + 1);
            }
        }
        return max(up.back(), down.back());
    }
};

下面介绍一个时间复杂度为O(n)的解法,题目标签给了动态规划和贪婪算法,那么第二个算法用的是贪婪算法,那么怎么个贪婪法呢?首先我们想到的是,如果想在i位置得到升序的摆动序列,那么我们自然希望前面对接的是最长的降序的摆动序列,反之亦然, 那么我们只需要遇到升序时,去前面的最长的降序长度加1即可更新升序的摆动序列,反之亦然。

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if (nums.empty()) return 0;
        int down = 1, up = 1, n = nums.size();
        for (int i = 1; i < n; ++i) {
            if (nums[i] > nums[i - 1]) up = down + 1;
            if (nums[i] < nums[i - 1]) down = up + 1;
        }
        return max(down, up);
    }
};

参考资料

[LeetCode] Wiggle Subsequence 摆动子序列

相关题目

LeetCode - 300. 最长上升子序列

你可能感兴趣的:(leetcode)