数据结构5:单调队列

单调队列

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

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

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

窗口位置 最小值 最大值
[1, 3, -1], -3, 5, 3, 6, 7 -1 1
1, [3, -1, -3], 5, 3, 6, 7 -3 3
1, 3, [-1, -3, 5], 3, 6, 7 -3 5
1, 3, -1, [-3, 5, 3], 6, 7 -3 5
1, 3, -1, -3, [5, 3, 6], 7 3 6
1, 3, -1, -3, 5, [3, 6, 7] 3 7
最后输出
-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;
}

代码如上

posted @ 2026-05-18 18:42  RonF02  阅读(1)  评论(0)    收藏  举报