kizumi_header_banner_img

是先学算法,先刷题,还是先写日记呢?

加载中

文章导读

单调队列


avatar
RonF02 2026年3月2日 41

例题:求滑动窗口的最大最小值

给定一个大小为 n≤1e6 的数组。
有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k 个数字。
每次滑动窗口向右移动一个位置。

更简而言之,对数组 [1 3 -1 -3 5 3 6 7] ,k = 3

窗口位置最小值最大值
[1, 3, -1] -3 5 3 6 7-11
1, [3, -1, -3] 5 3 6 7-33
1, 3, [-1, -3, 5] 3 6 7-35
1, 3, -1, [-3, 5, 3] 6 7-35
1, 3, -1, -3, [5, 3, 6] 736
1, 3, -1, -3, 5, [3, 6, 7]37

最后输出

-1 -3 -3 -3 3 3
3 3 5 5 6 7

这样就是一个单调队列的例题。

暴力朴素做法

遍历队列中的所有元素,时间复杂度O(n*k),1e12,明显tle,代码不贴了

优化

优化思路与单调栈相类似,先看暴力怎么做,再看有没有元素会被删掉,再看有没有单调性。这里以最小值举例。

以窗口 1, [3, -1, -3] 5 3 6 7 求最小值为例。 只要-3存在,那么 3 和 -1 永远不会被作为答案输出。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;
int a[N], q[N];

int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int n, k; cin >> n >> k;
    for (int i = 0; i < n; i ++ ) cin >> a[i];
    
    int hh = 0, tt = -1;
    for (int i = 0; i < n; i ++ ) 
    {
        // 判断队头是否已经滑出窗口
        if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
        while (hh <= tt && a[q[tt]] >= a[i]) tt -- ;
        q[ ++ tt] = i;
        if (i >= k - 1) cout << a[q[hh]] << ' ';
    }
    cout << '\n';
    hh = 0, tt = -1;
    for (int i = 0; i < n; i ++ ) 
    {
        // 判断队头是否已经滑出窗口
        if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
        while (hh <= tt && a[q[tt]] <= a[i]) tt -- ;
        q[ ++ tt] = i;
        if (i >= k - 1) cout << a[q[hh]] << ' ';
    }
    
    return 0;
}

代码如上



评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码
RonF02的博客

个人信息

avatar

24
文章
2
评论
1
用户