最长湍流子数组
1.题目描述
当 A 的子数组 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]。
- 也就是说,如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是湍流子数组。
返回 A 的最大湍流子数组的长度。
2.示例
示例 1:
输入:[9,4,2,10,7,8,8,1,9]
输出:5
解释:(A[1] > A[2] < A[3] > A[4] < A[5])
示例2
输入:[4,8,12,16]
输出:2
示例3
输入:[100]
输出:1
3.读题
其实,乍一看这个题目我也没太看懂,感觉挺抽象的。前两个条件让这个题目看起来非常复杂,既需要考虑奇偶,又需要考虑大小。幸好有第三个条件,让题意变得明朗了起来。
第三个条件是这么说的:如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是湍流子数组
。也就是说,假如在数组的相邻元素之间插入一个比较符号来描述两个元素之间的大小关系,那么湍流子数组的每个相邻比较符号是相反的。
仔细思考一下,这个条件包含着一些隐含的信息:
1.比较符号不包含等号,如果出现了相等的相邻数字,则湍流子数组中断
2.湍流子数组如果中断,则新的湍流子数组的长度需要重新计数
所以,题目要求是求湍流子数组的最大长度,而一个输入数组中可能包含多个湍流子数组,需要从头遍历,多次比较。
此外,由示例3可知,如果单个元素就可以作为一个湍流子数组,因此湍流子数组的长度至少为1。换句话说,如果当前子数组中只有一个元素,后续元素与其组成更长的子数组不需要考虑其之前的状态。
4.思路
由于湍流子数组是否能延续只与上一个比较符有关,因此可以从头到尾遍历来解决这个问题。
一开始考虑用一个布尔值来保存上一步的比较状态,再用一个整型来保存到上一步为止的最长湍流子数组长度。但是,如果上一步的比较结果是等于号,那么就无法用该布尔值来表示,需要使用额外的符号或者额外的判断。
但是,注意到,每一步与上一个数字之间的比较关系只有三种,即大于、小于和等于,那么可以用两个数字up和down来记录这一步的状态,其中up代表这个数字大于上一个数字时的最长湍流子数组长度,down代表这个数字小于这个数字时的最长湍流子数组长度:
-
1.如果这个数字比上一个数字要小,若需要延续湍流子数组,则上一个数字需要比上上个数字大才能延续。换句话说,这一步可以延续上一步比上上一步大的情况即上一步的up状态,所以这一步的down状态为上一步的up状态值+1。同时,这个数字比上个数字小,所以这一步就无法组成up的状态了,因此这一步的up湍状态为1即其本身。
-
2.同理,如果这个数字比上一个数字大,则这一步的up=上一步的down+1,这一步的down=1。
-
3.如果这个数字和上一个数字相等,那么即无法延续上一步的up子数组长度,也无法延续上一步的down子数组长度,所以这一步的up和down的状态只能为1,重新计算。
由于每一步都有可能会重置up或者down的值,因此每一步更新了up和down的值之后,都要更新一下最大子数组长度max的值。
5.代码
代码如下,应该还是很简单易懂的。
class Solution {
public int maxTurbulenceSize(int[] A) {
int up = 1;
int down = 1;
int max = 1;
for(int i=1;i<A.length;i++){
if(A[i]>A[i-1]){
up = down+1;
down = 1;
}else if(A[i]<A[i-1]){
down = up+1;
up = 1;
}else{
up = 1;
down = 1;
}
max = Math.max(max,Math.max(up,down));
}
return max;
}
}