Leetcode 1438 绝对差不超过限制的最长连续子数组:单调队列

文章内容

https://leetcode.cn/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/description/

一、题目解析

给定整数数组 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。

滑动窗口双单调队列推演

三、代码实现

java
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 生成

觉得不错?给作者点个赞吧

分类:题解

评论 (0)

无需注册,留言经站长审核后公开。

加载评论中…

Leetcode 1438 绝对差不超过限制的最长连续子数组:单调队列 · Yinchen 的博客