摆动序列的定义为:如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列,规定少于等于两个元素的序列也是摆动序列。下面尝试使用贪心和动态规划两种思想来解题。
贪心
若我们把数组的下标为 x 轴、值为 y 轴,在一个二维坐标系中描点连线画出 “函数” 图像,我们可以总是选择该 “函数” 的 “极值点” 作为摆动序列的一部分,就能使得该序列尽可能长(证明方法见力扣官方题解)。注意序列两端的元素也是符合题意的。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if(n <= 1)
return n;
int count = 1; // 最终结果初始化为1,因为默认最右边有一个极值
int curSub = 0; // 当前数与前一个数的差
int preSub = 0; // 前一个数与再前一个数的差
for(int i = 1; i < n; i ++) {
curSub = nums[i] - nums[i - 1];
if((curSub > 0 && preSub <= 0) || (curSub < 0 && preSub >= 0)) { // curSub与preSub异号时碰到极值,其中preSub >= 0的等于号是对最左边可能的极值和诸如0,0,1序列的特殊处理
count ++;
preSub = curSub; // preSub仅在遇到极值时更新(斜率符号改变)
}
}
return count;
}
};
动态规划
设 dp 状态 dp[0][i],表示前 i 个数中以 nums[i] 结尾的最长上升摆动序列长度;设 dp 状态 dp[1][i],表示前 i 个数中以 nums[i] 结尾的最长下降摆动序列长度。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if(n <= 1)
return n;
vector<vector<int>> dp(2, vector<int>(n, 1));
dp[0][0] = 1;
dp[1][0] = 1;
for(int i = 1; i < n; i ++) {
for(int j = 0; j < i; j ++) {
if(nums[i] > nums[j]) { // 将nums[i]尝试接到前面某个极小值后面,作为极大值
dp[0][i] = max(dp[0][i], dp[1][j] + 1);
}
if(nums[i] < nums[j]) { // 将nums[i]尝试接到前面某个极大值后面,作为极小值
dp[1][i] = max(dp[1][i], dp[0][j] + 1);
}
}
}
return max(dp[0][n - 1], dp[1][n - 1]);
}
};
当然我们还可以设 dp 状态 dp[0][i],表示数组 nums 的子数组 nums[0…i] 的最长上升摆动序列长度;设 dp 状态 dp[1][i],表示数组 nums 的子数组 nums[0…i] 的最长下降摆动序列长度(注意区分和上面状态定义的不同之处,这直接决定了算法的实现)。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if(n <= 1)
return n;
// 第0行表示子数组nums[0...i]的最长上升摆动序列长度,第1行表示子数组nums[0...i]的最长下降摆动序列长度
vector<vector<int>> dp(2, vector<int>(n, 1));
dp[0][0] = 1;
dp[1][0] = 1;
for(int i = 1; i < n; i ++) {
if(nums[i] > nums[i - 1]) { // 当前数大于前一个数时,即新的尾结点上升
dp[0][i] = dp[1][i - 1] + 1; // 在前一个最长下降摆动序列基础上上升
dp[1][i] = dp[1][i - 1]; // 当前数大于前一个数时,最长下降摆动序列长度与前一个数的最长下降摆动序列长度相同(即摆动序列末尾的数直接选前一个数一定能保证长度不变小)
} else if(nums[i] < nums[i - 1]) { // 当前数小于前一个数时,即新的尾结点下降(逻辑同上)
dp[0][i] = dp[0][i - 1];
dp[1][i] = dp[0][i - 1] + 1;
} else { // 当前数等于前一个数
dp[0][i] = dp[0][i - 1];
dp[1][i] = dp[1][i - 1];
}
}
return max(dp[0][n - 1], dp[1][n - 1]);
}
};