📅  最后修改于: 2023-12-03 15:27:35.584000             🧑  作者: Mango
给定一个整数数组 nums
,返回该数组中最长的 ZigZag 子数组的长度。一个 ZigZag 子数组可以通过交替的正负数构造,其中第一个数和最后一个数都是正数。
输入: nums = [1,7,4,9,2,5] 输出: 6 解释: 整个数组都是 wiggle 序列,因为差分数组中的元素来回切换。
输入: nums = [1,17,5,10,13,15,10,5,16,8] 输出: 7 解释: 这个子数组是 wiggle 序列:[5,10,13,15,10,5,16]。
输入: nums = [1,2,3,4,5,6,7,8,9] 输出: 2
我们可以用 $up[i]$ 表示第 $i$ 个数为结尾的最长的 ZigZag 子数组的长度,其中最后一个元素是个正数且上一个元素是个负数。我们还可以用 $down[i]$ 表示第 $i$ 个数为结尾的最长的 ZigZag 子数组的长度,其中最后一个元素是个负数且上一个元素是个正数。
对于第 $i$ 个元素 $nums[i]$,我们可以分为以下两种情况:
$nums[i]>nums[i-1]$,即 $nums[i]$ 是正数
$nums[i]<nums[i-1]$,即 $nums[i]$ 是负数。与情况1类似,我们可以分以下两种情况:
最终我们的答案就是 $\max{up[i],down[i]}$。
状态转移需要遍历整个数组,所以时间复杂度为 $O(n)$。
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
n = len(nums)
if n < 2:
return n
up, down = [0] * n, [0] * n
up[0], down[0] = 1, 1
for i in range(1, n):
if nums[i] > nums[i-1]:
up[i] = down[i-1] + 1
down[i] = down[i-1]
elif nums[i] < nums[i-1]:
down[i] = up[i-1] + 1
up[i] = up[i-1]
else:
up[i], down[i] = up[i-1], down[i-1]
return max(up[-1], down[-1])
我们可以通过观察数组中的数字序列来发现一些规律。具体来说,我们可以发现,如果当前数字和前一个数字构成的变化是符合ZigZag的定义的,即当前数字和前一个数字的差是正数,那么这个新数字可以加入到ZigZag子数组中。否则,如果当前数字和前一个数字的差是负数,那么这个新数字可以成为ZigZag子数组中的另一个数(因为它是正数),但是它不能与前一个数字构成一个新的ZigZag子数组。
我们可以定义一个变量 flag
,来表示正在构建的子数组的方向,如果 flag=1
表示当前正在构建的子数组是up,即最后一个元素比上一个元素大,反之为down。
我们可以看一下下面的例子:
nums = [1, 13, 7, 5, 3, 1, 2, 4, 6, 8, 10]
假设我们已经找到了第一个up子数组 [1,13]
,即我们已经知道了子数组的方向,接下来我们需要找到下一个数字:
如果下一个数字比最后一个数字小,那么下一个数字可以加入到子数组中,因为我们在构建ZigZag子数组时需要使用交替的正负数.
如果下一个数字比最后一个数字大,我们需要终止当前的子数组,然后从当前数字开始构建一个新的down子数组,并把当前数字作为这个新子数组的第二个元素。注意,这里我们不能加入上一个数字,因为上一个数字是正数,不能与下一个数字构成新的zigzag子数组。
如果下一个数字与上一个数字相等,则忽略,继续往下寻找。
然后我们就可以继续往下寻找,重复上述过程,直到我们找到了一个下降的数字并且我们需要构造一个新的up子数组。
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
n = len(nums)
if n < 2:
return n
flag, ans = 0, 1
for i in range(1, n):
if nums[i] > nums[i-1] and (flag == 0 or flag == -1):
ans += 1
flag = 1
elif nums[i] < nums[i-1] and (flag == 0 or flag == 1):
ans += 1
flag = -1
return ans
时间复杂度为$O(n)$。