题目描述

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

例如, [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


题解

这题题面说的啰里啰唆的,其实就一句话:给你一个序列,找出最长的一个子序列,其中子序列相邻两个数的大小是波形的(也就是大小大小大等等这样的)。

暴力法

用 dfs 枚举所有可能的子序列,然后看最长的是多少,这种方法显然会超时。

动态规划

每日算法系列【LeetCode 376】摆动序列_动态规划

动态规划+时间优化每日算法系列【LeetCode 376】摆动序列_动态规划_02


贪心法

其实这题还可以直接贪心做,考虑一段连续的上升序列,最优子序列一定是包括了首尾两个数的,因为首是最小的数,选了它才能给前一个数留出更大的上升空间,而尾是最大的数,选了它才能给下一个数留出更多的下降空间。

所以我们贪心的扫描一遍数组,遇到上升或者下降的转折点就选取这个数。而如果数组不升不降,也就是不变的话,就不用管它,因为这些相同的数里面只需要选取一个就行了。

每日算法系列【LeetCode 376】摆动序列_子序列_03

代码

动态规划(c++)


class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if (n <= 1) return n;
int dp[n][2], res = 1;
memset(dp, 0, sizeof dp);
dp[0][0] = dp[0][1] = 1;
for (int i = 1; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (nums[j] != nums[i]) {
int s = nums[j] < nums[i];
dp[i][s] = max(dp[i][s], dp[j][s^1]+1);
}
}
res = max(res, dp[i][0]);
res = max(res, dp[i][1]);
}
return res;
}
};


动态规划+时间优化(c++)


class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if (n <= 1) return n;
int dp[n][2];
memset(dp, 0, sizeof dp);
dp[0][0] = dp[0][1] = 1;
for (int i = 1; i < n; ++i) {
if (nums[i] == nums[i-1]) {
dp[i][0] = dp[i-1][0];
dp[i][1] = dp[i-1][1];
} else {
int s = nums[i] > nums[i-1];
dp[i][s] = max(dp[i-1][s], dp[i-1][s^1] + 1);
dp[i][s^1] = dp[i-1][s^1];
}
}
return max(dp[n-1][0], dp[n-1][1]);
}
};


动态规划+空间优化(c++)


class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if (n <= 1) return n;
int dp[2][2];
memset(dp, 0, sizeof dp);
dp[0][0] = dp[0][1] = 1;
for (int i = 1; i < n; ++i) {
if (nums[i] == nums[i-1]) {
dp[1][0] = dp[0][0];
dp[1][1] = dp[0][1];
} else {
int s = nums[i] > nums[i-1];
dp[1][s] = max(dp[0][s], dp[0][s^1]+1);
dp[1][s^1] = dp[0][s^1];
swap(dp[0][s], dp[1][s]);
swap(dp[0][s^1], dp[1][s^1]);
}
}
return max(dp[0][0], dp[0][1]);
}
};


贪心(c++)


class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if (n <= 1) return n;
int res = 1, pre_ord = -1;
for (int i = 1; i < n; ++i) {
if (nums[i] == nums[i-1]) continue;
int ord = nums[i] > nums[i-1];
if (ord != pre_ord) res++;
pre_ord = ord;
}
return res;
}
};


后记

鼠年快乐,新年献给大家的第一道题,尽量写的详细一点。

官方题解没有严谨的证明,虽然方法也是这 5 种,但是没有说清楚,不能令人信服。