#239 固定滑窗最值 困难

滑动窗口最大值

在 LeetCode 上查看 ↗

🎧 语音讲解

开车或通勤时可听,跟着思路走一遍

速度

📋 题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值

示例 1

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

💭 模拟答题者思考

1. 暴力法:对每个窗口遍历找最大值,O(nk)。问题:k 接近 n 时太慢。

2. 重复劳动在哪?相邻窗口共享 k-1 个元素,只有一个出队一个入队。每次重新扫太浪费。

3. 我需要一个能快速获取最大值、支持滑动更新的结构 → 单调队列:维护候选最大值的递减序列。

4. 单调队列的妙处:新元素入队时,比它小的「旧元素」永远不可能成为答案,直接弹出。队首一定最大。

🧠 变量语义(先读这三句再编码)

变量类型语义(三句法)
dqdeque<int>定义:候选最大值下标队列(值单调递减)
维护:队首始终是当前窗口的最大值下标
更新:入队时弹出所有 ≤ nums[i] 的旧元素;窗口右移时若队首滑出窗口则弹出
i - k + 1int定义:当前窗口的左边界下标
维护:随 i 递增
更新:每轮右移窗口时 +1

⌨️ 落码步骤

1. 初始化双端队列 dq(存下标)

2. 遍历 nums,先弹出窗口外元素:while dq[0] <= i - k: dq.popleft()

3. 维护单调递减:while dq and nums[dq[-1]] <= nums[i]: dq.pop()

4. dq.append(i)(新下标入队)

5. 当 i >= k-1(窗口形成),ans.append(nums[dq[0]])

💻 代码实现

from collections import deque

class Solution:
    def maxSlidingWindow(self, nums: list[int], k: int) -> list[int]:
        dq = deque()  # 存下标,值单调递减
        ans = []

        for i in range(len(nums)):
            # 1. 弹出滑出窗口的下标
            if dq and dq[0] <= i - k:
                dq.popleft()

            # 2. 维护单调递减:弹出所有 ≤ nums[i] 的旧元素
            while dq and nums[dq[-1]] <= nums[i]:
                dq.pop()

            # 3. 新下标入队
            dq.append(i)

            # 4. 窗口形成后,队首就是当前窗口最大值
            if i >= k - 1:
                ans.append(nums[dq[0]])

        return ans
class Solution {
public:
    vector maxSlidingWindow(vector& nums, int k) {
        deque dq;  // 存下标,值单调递减
        vector ans;

        for (int i = 0; i < nums.size(); i++) {
            // 1. 弹出滑出窗口的下标
            if (!dq.empty() && dq.front() <= i - k)
                dq.pop_front();

            // 2. 维护单调递减:弹出所有 <= nums[i] 的旧元素
            while (!dq.empty() && nums[dq.back()] <= nums[i])
                dq.pop_back();

            // 3. 新下标入队
            dq.push_back(i);

            // 4. 窗口形成后,队首就是当前窗口最大值
            if (i >= k - 1)
                ans.push_back(nums[dq.front()]);
        }
        return ans;
    }
};
// 时间 O(n),空间 O(k)

📈 复杂度分析

时间复杂度 O(n)
空间复杂度 O(k)

⚠️ 常见坑

弹出顺序:必须先弹出窗口外元素(dq[0] <= i-k),再维护单调性。

维护单调性要用 <= 而非 <:遇到相等值也要弹出旧元素,保证队首是「最近」的最大值(虽然不影响本题结果,但更规范)。

忘记窗口还没形成时不能记录答案:只有当 i >= k-1 时才把队首加入结果。

🔍 必测边界 Case

Case 1:k=1
nums = [1,-1,2], k = 1 → 输出 [1,-1,2](每个窗口就是单个元素)
Case 2:k=n
nums = [3,1,2], k = 3 → 输出 [3]
Case 3:单调递减数组
nums = [5,4,3,2,1], k = 3 → 输出 [5,4,3]