📅  最后修改于: 2023-12-03 15:25:26.945000             🧑  作者: Mango
给定一个整数数组 nums
,请找到其中最长的子数组,其数字中的最大值和最小值的差值不超过任意给定的丑陋数字 k
。
为了解决这个问题,我们可以使用滑动窗口来找到其中最长的子数组。
具体来说,我们可以维护两个指针 left
和 right
,分别表示窗口的左右边界,然后我们可以在一次遍历中不断移动右指针 right
,直到找到一个符合条件的子数组为止。在移动右指针的过程中,我们需要维护一个变量 min_val
表示当前窗口中的最小值,以及一个变量 max_val
表示当前窗口中的最大值,如果 max_val - min_val
大于丑陋数字 k
,则我们需要移动左指针 left
,直到窗口符合条件为止。
在移动左指针的过程中,我们可以使用一个变量 min_idx
记录当前窗口中最小值的下标,以及一个变量 max_idx
记录当前窗口中最大值的下标。当 left
移动之后,如果 min_idx
或者 max_idx
等于 left
,则我们需要重新找到窗口中的最小值或者最大值,并更新 min_idx
或者 max_idx
。
为了方便计算,我们可以使用两个单调队列 max_q
和 min_q
来维护窗口中的最大值和最小值,其中 max_q
是一个单调递减的队列,存储当前窗口中的最大值,min_q
是一个单调递增的队列,存储当前窗口中的最小值。每次移动右指针 right
的时候,我们可以把新的元素加入队列 max_q
和 min_q
中,并且在加入之前把队列中大于等于当前元素的部分弹出。每次移动左指针 left
的时候,我们可以把队列 max_q
和 min_q
中队首下标等于 left
的元素弹出。
最后,我们可以计算窗口的长度,如果长度大于之前找到的最大长度,则我们更新最大长度。
下面是参考代码实现,其中 max_q
和 min_q
分别表示单调队列。由于需要返回最大长度,因此我们可以在更新最大长度的时候同时更新窗口的左右边界。代码中用 res_l
和 res_r
分别表示当前找到的最长子数组的左右边界。
def max_length(nums, k):
n = len(nums)
max_q, min_q = [], []
left, right, res_l, res_r = 0, 0, 0, 0
for right in range(n):
while max_q and nums[right] > nums[max_q[-1]]:
max_q.pop()
while min_q and nums[right] < nums[min_q[-1]]:
min_q.pop()
max_q.append(right)
min_q.append(right)
while nums[max_q[0]] - nums[min_q[0]] > k:
left += 1
if max_q[0] < left:
max_q.pop(0)
if min_q[0] < left:
min_q.pop(0)
if right - left + 1 > res_r - res_l + 1:
res_l, res_r = left, right
return res_r - res_l + 1
代码中用一个单调队列来维护窗口中的最大值和最小值,因此时间复杂度为 $O(n)$。空间复杂度为 $O(n)$,因为最坏情况下窗口可能会扩展到长度为 $n$。