一、题目解析
给定整数数组 nums 和整数 limit,找出最长连续子数组,使得子数组中任意两个元素的绝对差 ≤ limit。返回这个最长子数组的长度。
以 nums = [8,2,4,7], limit = 4 为例:[2,4] 和 [4,7] 都满足要求,长度 2。[8,2] 不满足(|8-2|=6 > 4)。[2,4,7] 不满足(|7-2|=5 > 4)。答案是 2。
在一个窗口内,任意两元素之差 ≤ limit,等价于窗口最大值与最小值之差 ≤ limit。问题转化为:维护一个滑动窗口,动态获取窗口内的最大值和最小值,当差值超限时缩窗。
二、解题思路
3.1 两个队列协同工作
maxQ(递减队列)队头是窗口最大值,minQ(递增队列)队头是窗口最小值。只需要检查 maxQ.first - minQ.first > limit 就能判断窗口是否合法——因为窗口内任意元素对的最大跨度就是最大值减最小值。
当差值超限时缩窗:左指针右移,同时把小于新左指针的过期索引从两个队列头部移除。
3.2 完整推演
以 nums = [8,2,4,7], limit = 4 推一遍:
r=0, nums[0]=8:两个队列均入队 0。maxQ=[0], minQ=[0],max-min=0 ≤ 4。窗口 [8],ans=1。
r=1, nums[1]=2:
maxQ:8>2,维持递减,入队 1。maxQ=[0(8), 1(2)]
minQ:8>2,弹 0。入队 1。minQ=[1(2)]
窗口最大值 8,最小值 2。8-2=6 > 4,超限!
缩窗:l=1。maxQ.first=0 < l=1,弹。maxQ=[1(2)]。minQ.first=1 未过期。
重新检查:2-2=0 ≤ 4。窗口 [2],ans=max(1,1)=1。
r=2, nums[2]=4:
maxQ:2<4,弹 1。入队 2。maxQ=[2(4)]
minQ:2<4,维持递增,入队 2。minQ=[1(2), 2(4)]
4-2=2 ≤ 4。窗口 [2,4],ans=2。
r=3, nums[3]=7:
maxQ:4<7,弹 2。入队 3。maxQ=[3(7)]
minQ:4<7,入队 3。minQ=[1(2), 2(4), 3(7)]
7-2=5 > 4,超限!
缩窗:l=2。maxQ.first=3,未过期。minQ.first=1 < l=2,弹。minQ=[2(4), 3(7)]
7-4=3 ≤ 4。窗口 [4,7],ans=max(2,2)=2。
遍历结束,ans=2。

三、代码实现
class Solution { public int longestSubarray(int[] nums, int limit) { // maxQ:单调递减,队头是窗口最大值 Deque<Integer> maxQueue = new ArrayDeque<>(); // minQ:单调递增,队头是窗口最小值 Deque<Integer> minQueue = new ArrayDeque<>(); int l = 0; int ans = 0; for (int r = 0; r < nums.length; r++) { // 去尾:维护 maxQ 递减 while (!maxQueue.isEmpty() && nums[maxQueue.peekLast()] < nums[r]) { maxQueue.pollLast(); } // 去尾:维护 minQ 递增 while (!minQueue.isEmpty() && nums[minQueue.peekLast()] > nums[r]) { minQueue.pollLast(); } // 入队 maxQueue.offerLast(r); minQueue.offerLast(r); // 窗口最大值 - 最小值 > limit,缩窗 while (Math.abs(nums[maxQueue.peekFirst()] - nums[minQueue.peekFirst()]) > limit) { l++; // 去头:移除过期索引 if (maxQueue.peekFirst() < l) { maxQueue.pollFirst(); } if (minQueue.peekFirst() < l) { minQueue.pollFirst(); } } // 当前窗口长度 ans = Math.max(ans, r - l + 1); } return ans; }}每个元素最多入队一次、出队一次,时间复杂度 O(n)。两个队列最多各存 n 个索引,空间复杂度 O(n)。
本文由 AgentPlanFlow 生成