【力扣】978. 最长湍流子数组

以下为力扣官方题解

题目

A A A 的子数组$ A[i], A[i+1], …, A[j]$ 满足下列条件时,我们称其为湍流子数组:

  • i < = k < j i <= k < j i<=k<j,当 k k k 为奇数时, A [ k ] > A [ k + 1 ] A[k] > A[k+1] A[k]>A[k+1],且当 k k k 为偶数时, A [ k ] < A [ k + 1 ] A[k] < A[k+1] A[k]<A[k+1]
  • i < = k < j i <= k < j i<=k<j,当 k k k 为偶数时, A [ k ] > A [ k + 1 ] A[k] > A[k+1] A[k]>A[k+1] ,且当 k k k 为奇数时, A [ k ] < A [ k + 1 ] A[k] < A[k+1] A[k]<A[k+1]

也就是说,如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是湍流子数组。
返回 A A A 的最大湍流子数组的长度。

示例1

输入 [ 9 , 4 , 2 , 10 , 7 , 8 , 8 , 1 , 9 ] [9,4,2,10,7,8,8,1,9] [9,4,2,10,7,8,8,1,9]
输出 5 5 5
解释 ( A [ 1 ] > A [ 2 ] < A [ 3 ] > A [ 4 ] < A [ 5 ] ) (A[1] > A[2] < A[3] > A[4] < A[5]) (A[1]>A[2]<A[3]>A[4]<A[5])

示例2

输入 [ 4 , 8 , 12 , 16 ] [4,8,12,16] [4,8,12,16]
输出 2 2 2

示例3

输入 [ 100 ] [100] [100]
输出 1 1 1

提示

  1. 1 <= A.length <= 40000
  2. 0 <= A[i] <= 109

官方题解

思路一 滑动窗口

设数组 a r r arr arr 的长度为 n n n,窗口 [ l e f t , r i g h t ] ( 0 ≤ l e f t ≤ r i g h t ≤ n − 1 ) [left, right] (0 \le left \le right \le n-1) [left,right](0leftrightn1) 为当前的窗口,窗口内构成了一个「湍流子数组」。随后,我们要考虑下一个窗口的位置。

根据「湍流子数组」的定义,当 0 < r i g h t < n − 1 0<right<n−1 0<right<n1 时:

  • 如果 a r r [ r i g h t − 1 ] < a r r [ r i g h t ] arr[right-1] \lt arr[right] arr[right1]<arr[right] a r r [ r i g h t ] > a r r [ r i g h t + 1 ] arr[right] \gt arr[right+1] arr[right]>arr[right+1],则 [ l e f t , r i g h t + 1 ] [left, right+1] [left,right+1] 也构成了「湍流子数组」,因此需要将 r i g h t right right 右移一个单位;
  • 如果 a r r [ r i g h t − 1 ] > a r r [ r i g h t ] arr[right-1] \gt arr[right] arr[right1]>arr[right] a r r [ r i g h t ] < a r r [ r i g h t + 1 ] arr[right] \lt arr[right+1] arr[right]<arr[right+1],同理,也需要将 r i g h t right right 右移一个单位;
  • 否则, [ r i g h t − 1 , r i g h t + 1 ] [right-1, right+1] [right1,right+1] 无法构成「湍流子数组」,当 l e f t < r i g h t left \lt right left<right [ l e f t , r i g h t + 1 ] [left, right+1] [left,right+1] 也无法构成「湍流子数组」,因此需要将 l e f t left left 移到 r i g h t right right,即令 l e f t = r i g h t left=right left=right

此外,我们还需要特殊考虑窗口长度为 1 1 1 (即 l e f t left left r i g h t right right 相等的情况):只要 a r r [ r i g h t ] ≠ a r r [ r i g h t + 1 ] arr[right] \neq arr[right+1] arr[right]=arr[right+1],就可以将 r i g h t right right 右移一个单位;否则, l e f t left left r i g h t right right 都要同时右移。

代码

class Solution {
    public int maxTurbulenceSize(int[] arr) {
        int n = arr.length;
        int ret = 1;
        int left = 0, right = 0;

        while (right < n - 1) {
            if (left == right) {
                if (arr[left] == arr[left + 1]) {
                    left++;
                }
                right++;
            } else {
                if (arr[right - 1] < arr[right] && arr[right] > arr[right + 1]) {
                    right++;
                } else if (arr[right - 1] > arr[right] && arr[right] < arr[right + 1]) {
                    right++;
                } else {
                    left = right;
                }
            }
            ret = Math.max(ret, right - left + 1);
        }
        return ret;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 为数组的长度。窗口的左右端点最多各移动 n n n 次。
  • 空间复杂度: O ( 1 ) O(1) O(1)。只需要维护常数额外空间。

思路二

也可以使用动态规划的方法计算最长湍流子数组的长度。

d p [ i ] [ 0 ] dp[i][0] dp[i][0] 为以 a r r [ i ] arr[i] arr[i] 结尾,且 a r r [ i − 1 ] > a r r [ i ] arr[i−1]>arr[i] arr[i1]>arr[i] 的「湍流子数组」的最大长度; d p [ i ] [ 1 ] dp[i][1] dp[i][1] 为以 a r r [ i ] arr[i] arr[i] 结尾,且 a r r [ i − 1 ] < a r r [ i ] arr[i−1]<arr[i] arr[i1]<arr[i] 的「湍流子数组」的最大长度。显然,以下标 0 0 0 结尾的「湍流子数组」的最大长度为 1 1 1,因此边界情况为 d p [ 0 ] [ 0 ] = d p [ 0 ] [ 1 ] = 1 dp[0][0]=dp[0][1]=1 dp[0][0]=dp[0][1]=1

i > 0 i>0 i>0 时,考虑 a r r [ i − 1 ] arr[i−1] arr[i1] a r r [ i ] arr[i] arr[i] 之间的大小关系:

  • 如果 a r r [ i − 1 ] > a r r [ i ] arr[i−1]>arr[i] arr[i1]>arr[i],则如果以下标 i − 1 i-1 i1 结尾的子数组是「湍流子数组」,应满足 i − 1 = 0 i−1=0 i1=0,或者当 i − 1 > 0 i−1>0 i1>0 a r r [ i − 2 ] < a r r [ i − 1 ] arr[i−2]<arr[i−1] arr[i2]<arr[i1],因此 d p [ i ] [ 0 ] = d p [ i − 1 ] [ 1 ] + 1 dp[i][0]=dp[i−1][1]+1 dp[i][0]=dp[i1][1]+1
  • 如果 a r r [ i − 1 ] < a r r [ i ] arr[i−1]<arr[i] arr[i1]<arr[i],则如果以下标 i − 1 i-1 i1 结尾的子数组是「湍流子数组」,应满足 i − 1 = 0 i−1=0 i1=0,或者当 i − 1 > 0 i−1>0 i1>0 a r r [ i − 2 ] > a r r [ i − 1 ] arr[i−2]>arr[i−1] arr[i2]>arr[i1],因此 d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + 1 dp[i][1]=dp[i−1][0]+1 dp[i][1]=dp[i1][0]+1
  • 如果 a r r [ i − 1 ] = a r r [ i ] arr[i−1]=arr[i] arr[i1]=arr[i],则 a r r [ i − 1 ] arr[i−1] arr[i1] a r r [ i ] arr[i] arr[i] 不能同时出现在同一个湍流子数组中,因此 d p [ i ] [ 0 ] = d p [ i ] [ 1 ] = 1 dp[i][0]=dp[i][1]=1 dp[i][0]=dp[i][1]=1

最终, d p dp dp 数组的最大值即为所求的答案。

代码

class Solution {
    public int maxTurbulenceSize(int[] arr) {
        int n = arr.length;
        int[][] dp = new int[n][2];
        dp[0][0] = dp[0][1] = 1;
        for (int i = 1; i < n; i++) {
            dp[i][0] = dp[i][1] = 1;
            if (arr[i - 1] > arr[i]) {
                dp[i][1] = dp[i - 1][0] + 1;
            } else if (arr[i - 1] < arr[i]) {
                dp[i][0] = dp[i - 1][1] + 1;
            }
        }

        int ret = 1;
        for (int i = 0; i < n; i++) {
            ret = Math.max(ret, dp[i][0]);
            ret = Math.max(ret, dp[i][1]);
        }
        return ret;
    }
}

优化代码

上述实现的空间复杂度是 O ( n ) O(n) O(n)。注意到当 i > 0 i>0 i>0 时,下标 i i i 处的 d p dp dp 值只和下标 i − 1 i−1 i1 处的 d p dp dp 值有关,因此可以用两个变量 d p 0 dp0 dp0 d p 1 dp1 dp1​ 代替 d p [ i ] [ 0 ] dp[i][0] dp[i][0] d p [ i ] [ 1 ] dp[i][1] dp[i][1],将空间复杂度降到 O ( 1 ) O(1) O(1)

class Solution {
    public int maxTurbulenceSize(int[] arr) {
        int ret = 1;
        int n = arr.length;
        int dp0 = 1, dp1 = 1;
        for (int i = 1; i < n; i++) {
            if (arr[i - 1] > arr[i]) {
                dp0 = dp1 + 1;
                dp1 = 1;
            } else if (arr[i - 1] < arr[i]) {
                dp1 = dp0 + 1;
                dp0 = 1;
            } else {
                dp0 = 1;
                dp1 = 1;
            }
            ret = Math.max(ret, dp0);
            ret = Math.max(ret, dp1);
        }
        return ret;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 为数组的长度。需要遍历数组 a r r arr arr 一次,计算 d p dp dp 的值。
  • 空间复杂度: O ( 1 ) O(1) O(1)。使用空间优化的做法,只需要维护常数额外空间。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值