LeetCode376-摆动序列

题目链接

英文链接:https://leetcode.com/problems/wiggle-subsequence/

中文链接:https://leetcode-cn.com/problems/wiggle-subsequence/

题目详述

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

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

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

示例 1:

1
2
3
输入: [1,7,4,9,2,5]
输出: 6
解释: 整个序列均为摆动序列。

示例 2:

1
2
3
输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。

示例 3:

1
2
输入: [1,2,3,4,5,6,7,8,9]
输出: 2

进阶:
你能否用 O(n) 时间复杂度完成此题?

题目详解

方法一:贪心。

  • 用一个标志来记录是上升还是下降。
  • 符合条件结果就递增,标志取反。
  • 开始是上升扫一遍,开始是下降扫一遍(实际上判断开始是上升还是下降后扫一遍就可以)。
  • 两者的较大值就是最终结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class LeetCode_00376 {

public int wiggleMaxLength(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
return Math.max(wiggle(nums, true), wiggle(nums, false));
}

private int wiggle(int[] nums, boolean flag) {
int res = 1;
for (int i = 1; i < nums.length; ++i) {
if (flag) {
if (nums[i] > nums[i - 1]) {
++res;
flag = false;
}
} else {
if (nums[i] < nums[i - 1]) {
++res;
flag = true;
}
}
}
return res;
}
}

方法二:动态规划。

  • 运用两个辅助数组 up[] 和 down[]。
  • up[i] 表示到索引 i 处上升(nums[i] > nums[i - 1)的最长摆动序列的长度。
  • down[i] 表示到索引 i 处下降(nums[i] < nums[i - 1)的最长摆动序列的长度。
  • nums[i] > nums[i - 1] 时,up[i] = down[i - 1] + 1
  • nums[i] < nums[i - 1] 时,down[i] = up[i - 1] + 1
  • 因为 up[i] 和 down[i] 只与上一个状态有关,可以进行优化,用两个变量来压缩空间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class LeetCode_00376 {

public int wiggleMaxLength(int[] nums) {
if (nums.length < 2) {
return nums.length;
}
int up = 1;
int down = 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(up, down);
}
}