滑动窗口最大值:单调队列高效解法

滑动窗口最大值是数组类问题中的经典难题,本文将带你用单调队列实现时间复杂度 O (n) 的高效解法,彻底搞定这道题。

一、题目回顾

给定整数数组nums和窗口大小k,窗口从数组最左侧滑动到最右侧,返回每个窗口内的最大值。

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

二、为什么不用暴力法?

暴力法的思路是 "遍历每个窗口,逐个比较求最大值",但时间复杂度是 O (nk)(n 是数组长度),当 n 和 k 很大时会超时。

我们需要更高效的方法 ------单调队列,它能在 O (1) 时间内获取窗口最大值。

三、单调队列解法思路

单调队列的核心是维护一个 "单调递减" 的双端队列 ,队列中存储的是数组元素的下标(而非值),通过以下 3 个步骤保证队列的有效性:

步骤 1:右侧元素入队(维护单调性)

遍历到元素nums[i]时,从队尾弹出所有小于等于nums[i]的元素 ,再将i加入队尾。

  • 这样队列从队首到队尾是 "单调递减" 的,队首始终是当前窗口的最大值下标。

步骤 2:左侧元素出队(保证窗口范围)

计算当前窗口的左端点left = i - k + 1,如果队首下标小于left,说明队首元素已经 "滑出窗口",需要从队首弹出。

步骤 3:记录窗口最大值

left >= 0(窗口完全形成)时,队首下标对应的元素就是当前窗口的最大值,将其存入结果数组。

四、完整代码实现(C++)

cpp

复制代码
#include <vector>
#include <deque>
using namespace std;

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> ans(n - k + 1); // 结果数组长度 = 窗口数
        deque<int> q; // 双端队列,存储下标

        for (int i = 0; i < n; ++i) {
            // 1. 右侧入队:维护队列单调递减
            while (!q.empty() && nums[q.back()] <= nums[i]) {
                q.pop_back();
            }
            q.push_back(i);

            // 2. 左侧出队:移除窗口外的元素
            int left = i - k + 1;
            if (q.front() < left) {
                q.pop_front();
            }

            // 3. 记录当前窗口最大值
            if (left >= 0) {
                ans[left] = nums[q.front()];
            }
        }
        return ans;
    }
};

五、代码解析

以示例nums = [1,3,-1,-3,5,3,6,7], k=3为例:

  • i=1(元素 3):队尾弹出 1 的下标 0,队列存 1,窗口左端点left=-1(未形成窗口)。
  • i=2(元素 - 1):队尾直接加入 2,窗口左端点left=0(窗口形成),ans 0 = nums 1 = 3。
  • i=4(元素 5):队尾弹出 - 3(下标 3)、-1(下标 2)、3(下标 1),队列存 4,窗口左端点left=2,ans2 = nums4 = 5。

六、复杂度分析

  • 时间复杂度:O (n)。每个元素最多入队和出队各一次,总操作次数是 O (n)。
  • 空间复杂度:O (k)。队列中最多存储 k 个元素(窗口大小)。

七、总结

单调队列是解决 "滑动窗口最值" 问题的利器,核心是通过维护队列的单调性,在 O (1) 时间内获取窗口最值。掌握这个思路后,类似的问题(如滑动窗口最小值)也能举一反三。

相关推荐
.道阻且长.7 小时前
2.LeetCode算法习题讲解--双指针--复写零
算法·leetcode·职场和发展
To_OC9 小时前
LC 438 找到所有字母异位词:暴力超时后,我靠滑动窗口一招搞定
javascript·算法·leetcode
白狐_7989 小时前
408数据结构第5章:二叉树遍历序列题——技巧、判断与真题型总结
数据结构
Forever Nore12 小时前
学完C语言力扣第一题做不来正常吗
数据结构·算法
hansang_IR12 小时前
【题解】LC:倍增 / 区间并查集(Range Parallel Unionfind)
c++·算法·并查集
Tisfy14 小时前
LeetCode 3731.找出缺失的元素:哈希 / 排序
算法·leetcode·哈希算法·排序·哈希表
lucas_AI14 小时前
Q-CueGraph:你的多模态大模型会 zoom,但真的知道该看哪儿吗?
人工智能·算法
kaixin_啊啊15 小时前
test_机器学习算法学习
学习·算法·机器学习
liulilittle15 小时前
MOE路由:路由(logits: top-k/8)
c++·人工智能·算法·机器学习·llm