引入定义
如果连续数字之间的差严格地在正数和负数之间交替,称为摆动数组。
举几个例子。[1,7,4,9,2,5]
是一个典型的摆动数组,它就像颠簸的波浪;[1,7,4,5,5]
不是摆动数组,因为它不是严格的上下摆动;[1]
和[1,5]
都符合定义,是摆动数组。
题目两则
Q1. 返回数组所包含的最大连续摆动数组的长度。
输入:[9,4,2,10,7,8,8,1,9]
输出:5
解释:[4,2,10,7,8]长度为2
Q2. 返回数组所包含的最大摆动数组的长度,不要求连续。
输入:[1,17,5,10,13,15,10,5,16,8]
输出:7
解释:[1,17,10,13,10,16,8]长度为7
答案两则
- 摆动数组的答案比较模板化———用两个变量记录此时摆动数组的长度情况(up, down)
- up的意义是,遍历到该处时,假设摆动数组正处于上升状态,它的长度;down的意义是,遍历到该处时,假设摆动数组正处于下降状态,它的长度
- 必须要理解的是,这两个摆动数组并不是同一个,因为我们假设了其结尾分别处于上升/下降状态
- 关键还在于这两个状态的转换,即up和down交叉转换(联系股票问题)
- 因为Q1要求连续,因此当摆动数组断裂时,up/down要重置为1;而Q2不要求连续,即摆动数组隐藏在整个数组中,up/down不需要重置,只需要继承(关键区别)
class Solution {
public int maxTurbulenceSize(int[] arr) {
int up = 1;
int down = 1;
int res = 1;
for(int i = 1; i < arr.length; i++) {
if(arr[i] > arr[i - 1]) {
up = down + 1;
down = 1;
} else if(arr[i] < arr[i - 1]) {
down = up + 1;
up = 1;
} else {
up = 1;
down = 1;
}
res = Math.max(res, Math.max(up, down));
}
return res;
}
}
class Solution {
public int maxTurbulenceSize(int[] arr) {
int up = 1;
int down = 1;
for(int i = 1; i < arr.length; i++) {
if(arr[i] > arr[i - 1]) {
up = down + 1;
}
if(arr[i] < arr[i - 1]) {
down = up + 1;
}
}
return Math.max(up, down);
}
}
思考探究
等一下,这种状态的交叉转化,让我们想到了经典的股票问题(戳这里),因此所谓的up/down模板,会不会就是动态规划经过滚动数组优化的结果?
是的。
下面我们从动态规划的角度,来还原出这道题完整的思路演化过程。
dp[i][0]
的含义是,以i处为结尾的且结尾处于上升状态的摆动数组的最大长度
dp[i][1]
的含义是,以i处为结尾的且结尾处于下降状态的摆动数组的最大长度
// 状态转移
if(arr[i] > arr[i - 1]) {
dp[i][0] = dp[i][1] + 1;
}
if(arr[i] < arr[i - 1]) {
dp[i][1] = dp[i][0] + 1;
}
// 滚动优化
if(arr[i] > arr[i - 1]) {
dp[0] = dp[1] + 1;
}
if(arr[i] < arr[i - 1]) {
dp[1] = dp[0] + 1;
}
一点理解
动态规划中,定义出dp[i][0/1]的含义是关键点也是难点。记住一件事,dp[i]的含义是“此时/此地完成了i的变化后,所处状态(0/1)所对应的值”,关键在于“状态”;
在股票问题中,dp[i][0/1]的含义倾向于状态(持股/不持股)而非动作(买入/卖出);在摆动数组中,dp[i][0/1]的含义也是倾向于状态(上升下降的状态)而非动作(上升下降的动作)———理解状态二字,至关重要。
E N D END END