LeetCode C++ 376. Wiggle Subsequence【Dynamic Programming】中等

memcpy0發表於2020-12-26

A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.

For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.

Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.

Example 1:

Input: [1,7,4,9,2,5]
Output: 6
Explanation: The entire sequence is a wiggle sequence.

Example 2:

Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
Explanation: There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].

Example 3:

Input: [1,2,3,4,5,6,7,8,9]
Output: 2

Follow up: Can you do it in O(n) time?

題意:如果連續數字之間的嚴格地在正數和負數之間交替,則數字序列稱為擺動序列。第一個差(如果存在的話)可能是正數或負數。少於兩個元素的序列也是擺動序列。

例如, [1,7,4,9,2,5] 是一個擺動序列,因為差值 (6,-3,5,-7,3) 是正負交替出現的。相反 [1,4,7,2,5][1,7,4,5,5] 不是擺動序列,第一個序列是因為它的前兩個差值都是正數,第二個序列是因為它的最後一個差值為零。

給定一個整數序列,返回作為擺動序列的最長子序列的長度。 通過從原始序列中刪除一些(也可以不刪除)元素來獲得子序列,剩下的元素保持其原始順序。


解法 動態規劃

連續數字之間的嚴格地在正數和負數之間交替,這種說法不太直觀——無非是 A[i - 1] < A[i] > A[i + 1] 或者 A[i - 1] > A[i] < A[i + 1] 這樣的變化序列。

應用最長遞增子序列的思想,得到 O ( n 2 ) O(n^2) O(n2) 的一個解法。具體程式碼如下:

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if (nums.empty()) return 0;
        int n = nums.size(), ans = 1;
        //dp[i][0]表示以nums[i]為上升結尾的最長擺動序列長度
        //dp[i][1]表示以nums[i]為下降結尾的最長擺動序列長度
        vector<vector<int>> dp(n, vector<int>(2));
        dp[0][0] = dp[0][1] = 1;
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < i; ++j) { 
                if (nums[i] < nums[j]) dp[i][1] = max(dp[i][1], dp[j][0] + 1); //能夠以nums[i]為下降結尾
                else if (nums[i] > nums[j]) dp[i][0] = max(dp[i][0], dp[j][1] + 1); //能夠以nums[i]為上升結尾
            }
            ans = max(ans, max(dp[i][0], dp[i][1]));
        }
        return ans;
    }   
};

執行效率如下:

執行用時:28 ms, 在所有 C++ 提交中擊敗了6.27% 的使用者
記憶體消耗:7.9 MB, 在所有 C++ 提交中擊敗了5.06% 的使用者

優化為 O ( n ) O(n) O(n) 演算法:

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if (nums.empty()) return 0;
        int n = nums.size(), ans = 1; 
        vector<vector<int>> dp(n, vector<int>(2));
        dp[0][0] = dp[0][1] = 1;
        for (int i = 1; i < n; ++i) {
            if (nums[i] > nums[i - 1]) {//nums[i-1]到nums[i]上升
                dp[i][0] = max(dp[i - 1][1] + 1, dp[i - 1][0]);
                dp[i][1] = dp[i - 1][1];
            } else if (nums[i] < nums[i - 1]) {
                dp[i][0] = dp[i - 1][0];
                dp[i][1] = max(dp[i - 1][0] + 1, dp[i - 1][1]);
            } else {
                dp[i][0] = dp[i - 1][0];
                dp[i][1] = dp[i - 1][1];
            }
            ans = max(ans, max(dp[i][0], dp[i][1]));
        }
        return ans;
    }   
};

執行效率如下:

執行用時:4 ms, 在所有 C++ 提交中擊敗了48.95% 的使用者
記憶體消耗:7.7 MB, 在所有 C++ 提交中擊敗了5.06% 的使用者

相關文章