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

滑动窗口最大值是数组类问题中的经典难题,本文将带你用单调队列实现时间复杂度 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) 时间内获取窗口最值。掌握这个思路后,类似的问题(如滑动窗口最小值)也能举一反三。

相关推荐
Tisfy33 分钟前
LeetCode 0301.删除无效的括号:二进制枚举 / 回溯
算法·leetcode·字符串·题解·回溯·括号匹配·二进制枚举
Nil20841 分钟前
leetcode 322零钱兑换
算法·leetcode·职场和发展
海绵宝宝转agent1 小时前
2026-10-9 leetcode100刷题+面经整理
笔记·算法·面试
yi0111 小时前
DAY24: LeetCode 167:两数之和 II|从暴力枚举到左右双指针
笔记·python·算法·leetcode·二分查找·双指针
木井巳1 小时前
【BFS 解决拓扑排序】课程表
java·算法·leetcode·广度优先·宽度优先·推荐算法·图搜索算法
Logic1011 小时前
C语言/数据结构数位DP题解:平滑排列数字统计——区间内相邻位差不超过K的数字个数
c语言·数据结构·动态规划·时间复杂度·数位dp·算法题·前导零处理
扶风ff1 小时前
练题簿在线免费刷题:会员线下组卷、Word 试卷与成绩导入,课堂检测更方便
java·开发语言·算法·小程序·word
yi0111 小时前
LeetCode 15:三数之和|排序 + 双指针,如何避免重复答案?
笔记·python·算法·leetcode·双指针
-dzk-2 小时前
【多维动态规划】LC 1143.最长公共子序列
算法·动态规划
C++ 老炮儿的技术栈2 小时前
char (*csConnectName)[256]; 和 char csConnectName [256] 有什么区别
java·c语言·开发语言·c++·人工智能·算法·c