1.题目链接:
给定一个整数数组 arr ,返回 arr 的 最大湍流子数组的长度 。
如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是 湍流子数组 。
更正式地来说,当 arr 的子数组 A[i], A[i+1], ..., A[j] 满足仅满足下列条件时,我们称其为湍流子数 组:
若 i <= k < j :当 k 为奇数时, A[k] > A[k+1],且 当 k 为偶数时,A[k] < A[k+1];
或 若 i <= k < j :当 k 为偶数时,A[k] > A[k+1] ,且 当 k 为奇数时, A[k] < A[k+1]。
示例 1:
输入:arr = [9,4,2,10,7,8,8,1,9]
输出:5
解释:arr[1] > arr[2] < arr[3] > arr[4] < arr[5]
示例 2:
输入:arr = [4,8,12,16]
输出:2
示例 3:
输入:arr = [100]
输出:1
提示:
1 <= arr.length <= 4 * 10^4
0 <= arr[i] <= 10^9
3. 解法(动态规划):
算法思路:
1. 状态表示:
我们先尝试定义状态表示为:
dp[i] 表示「以 i 位置为结尾的最长湍流数组的长度」。
但是,问题来了,如果状态表示这样定义的话,以 i 位置为结尾的最长湍流数组的长度我们没法 从之前的状态推导出来。因为我们不知道前一个最长湍流数组的结尾处是递增的,还是递减的。因 此,我们需要状态表示能表示多一点的信息:要能让我们知道这一个最长湍流数组的结尾是「递 增」的还是「递减」的。
因此需要两个 dp 表:
f[i] 表示:以 i 位置元素为结尾的所有子数组中,最后呈现「上升状态」下的最长湍流数组的长度;
g[i] 表示:以 i 位置元素为结尾的所有子数组中,最后呈现「下降状态」下的最长湍流数组的长度。
2. 状态转移方程:
对于 i 位置的元素 arr[i] ,有下面两种情况:
i. | arr[i] > arr[i - 1] :如果 i 位置的元素比 i - 1 位置的元素大,说明接下来 |
应该去找 i -1 位置结尾,并且 i - 1 位置元素比前一个元素小的序列,那就是 g[i - 1] 。更新 f[i] 位置的值:f[i] = g[i - 1] + 1 ;
ii. | arr[i] < arr[i - 1] :如果 i 位置的元素比 i - 1 位置的元素小,说明接下来 |
应该去找 i - 1 位置结尾,并且 i - 1 位置元素比前一个元素大的序列,那就是 f[i - 1] 。更新 g[i] 位置的值:g[i] = f[i - 1] + 1 ;
iii. | arr[i] == arr[i - 1] :不构成湍流数组。 |
3. 初始化:
所有的元素「单独」都能构成一个湍流数组,因此可以将 dp 表内所有元素初始化为 1 。
由于用到前面的状态,因此我们循环的时候从第二个位置开始即可。
4. 填表顺序:
毫无疑问是「从左往右,两个表一起填」。
5. 返回值:
应该返回「两个 dp 表里面的最大值」,我们可以在填表的时候,顺便更新一个最大值。
Java算法代码:
class Solution {
public int maxTurbulenceSize(int[] arr) {
// 1. 创建dp表
// 2.初始化
// 3.填表
// 4.返回值
int n = arr.length;
int[] f = new int[n];
int[] g = new int[n];
for(int i = 0; i < n; i++) f[i] = g[i] =1;
int ret = 1;
for(int i =1; i< n; i++){
if(arr[i-1] < arr[i]) f[i] = g[i-1] + 1;
else if(arr[i - 1] > arr[i]) g[i] = f[i - 1] + 1;
ret = Math.max(ret, Math.max(f[i],g[i]));
}
return ret;
}
}
运行结果:
动态规划: