Description:
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.
Examples:
Input: [1,7,4,9,2,5]
Output: 6
The entire sequence is a wiggle sequence.
Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].
Input: [1,2,3,4,5,6,7,8,9]
Output: 2
Follow up:
Can you do it in O(n) time?
问题描述:
如果一个序列中连续的数之间的差值在正数和负数之间来回交替,那么这个序列被称作摆动序列。如果存在第一个差值的话,可能为正数或者负数。长度小于2的序列被认为是摆动序列。
例如 [1,7,4,9,2,5]为摆动序列。而[1,4,7,2,5] 和[1,7,4,5,5]不是摆动序列
给定一组数,返回最长的摆动子序列。注意序列是由原数组删除一些元素(也可以不删除)并且保留元素顺序得到的。
问题分析:
这题大致有两种解法,贪心和动态规划
如果想详细了解这两种解法是如何实现的,建议看下这个链接,讲得非常清楚:
https://leetcode.com/problems/wiggle-subsequence/solution/
1.对于贪心,举个例子
[1, 2, 5, 7, 5, 4, 3, 5]
最长摆动子序列为[1, 7, 3, 5],长度为4
选择的原则是先将位于1处看作”下降”(针对这个例子,也可以是上升),然后在上升中找最大值,到了最大值后,下个元素从下降中的最小元素找,依次类推
2.对于动规,关键是两个状态和对应的转换
状态
1. up[i],位于i处,处于上升所能获得的最大摆动子序列长度
2. down[i],位于i处,处于下降所能获得的最大摆动子序列长度
转换
1. 当nums[i] > nums[j] && i > j,up[i] = down[j] + 1
2. 当nums[i] < nums[j] && i > j,down[i] = up[j] + 1
注意这里的动规是一个最原始的版本,还可以对其扩展,详细的话最好仔细看一下链接的文章
以下列举一些关键的解法
解法1(naive dp):
public class Solution {
public int wiggleMaxLength(int[] nums) {
if (nums.length < 2)
return nums.length;
int[] up = new int[nums.length];
int[] down = new int[nums.length];
for (int i = 1; i < nums.length; i++) {
for(int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
up[i] = Math.max(up[i],down[j] + 1);
} else if (nums[i] < nums[j]) {
down[i] = Math.max(down[i],up[j] + 1);
}
}
}
return 1 + Math.max(down[nums.length - 1], up[nums.length - 1]);
}
}
解法2(扩展后的线性dp):
public class Solution {
public int wiggleMaxLength(int[] nums) {
if (nums.length < 2)
return nums.length;
int[] up = new int[nums.length];
int[] down = new int[nums.length];
up[0] = down[0] = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[i - 1]) {
up[i] = down[i - 1] + 1;
down[i] = down[i - 1];
} else if (nums[i] < nums[i - 1]) {
down[i] = up[i - 1] + 1;
up[i] = up[i - 1];
} else {
down[i] = down[i - 1];
up[i] = up[i - 1];
}
}
return Math.max(down[nums.length - 1], up[nums.length - 1]);
}
}
解法3(空间优化后的dp):
public class Solution {
public int wiggleMaxLength(int[] nums) {
if (nums.length < 2)
return nums.length;
int down = 1, up = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[i - 1])
up = down + 1;
else if (nums[i] < nums[i - 1])
down = up + 1;
}
return Math.max(down, up);
}
}
解法4(贪心):
public class Solution {
public int wiggleMaxLength(int[] nums) {
if (nums.length < 2)
return nums.length;
int prevdiff = nums[1] - nums[0];
int count = prevdiff != 0 ? 2 : 1;
for (int i = 2; i < nums.length; i++) {
int diff = nums[i] - nums[i - 1];
if ((diff > 0 && prevdiff <= 0) || (diff < 0 && prevdiff >= 0)) {
count++;
prevdiff = diff;
}
}
return count;
}
}