题目描述
给你一个整数数组 nums
,和一个表示限制的整数 limit
,
请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit
。
如果不存在满足条件的子数组,则返回 0
。
样例1
输入:nums = [8,2,4,7], limit = 4
输出:2
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4.
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4.
因此,满足题意的最长子数组的长度为 2 。
样例2
输入:nums = [10,1,2,4,7,2], limit = 5
输出:4
解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。
样例3
输入:nums = [4,2,2,2,4,4,2,2], limit = 0
输出:3
限制
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
0 <= limit <= 10^9
算法
(单调队列) $O(n)$
- 单调队列不知道的话可以先看这个题
- 基本思想就是使用双向队列来维护某段区间里的最值
- 开
2
个队列,分别维护[j, i]
区间里的最大值和最小值。- 当读入一个新的数
num[i]
, 首先更新队列的队尾, 保证队列里不会存在逆序。 - 若
2
个队头之差大于limit
则将j
后移;- 后移
j
的同时要处理j
已滑出队头的情况,即需要更新队头保证队头处于[j, i]
区间。
- 后移
- 当读入一个新的数
- 最后求
ans = max(ans, i - j + 1)
时间复杂度
- 对于单调队列,每个数最多进队
1
次, 出队1
次, 最坏情况是所有数都相同且limit = 0
会进行2n
次进队出队操作,故时间复杂度为 $O(n)$
空间复杂度
2
个队列需要额外的存储空间是 $O(2n)$, 故空间复杂度为 $O(n)$
Go 代码
const N int = 1e5 + 10
var q1, q2 [N]int
func max(a, b int) int {
if a > b {
return a
}
return b
}
func longestSubarray(nums []int, limit int) int {
h1, t1 := 0, -1
h2, t2 := 0, -1
j := 0
ans := 0
n := len(nums)
for i:= 0; i < n; i ++ {
a := nums[i]
for h1 <= t1 && nums[q1[t1]] <= a {
t1 --
}
for h2 <= t2 && nums[q2[t2]] >= a {
t2 --
}
t1 ++
t2 ++
q1[t1] = i
q2[t2] = i
for h1 <= t1 && h2 <= t2 && (nums[q1[h1]] - nums[q2[h2]] > limit) {
j ++
for h1 <= t1 && q1[h1] < j {
h1 ++
}
for h2 <= t2 && q2[h2] < j {
h2 ++
}
}
ans = max(ans, i - j + 1)
}
return ans
}