数据结构学习NOTE - 单调队列

单调队列

P1886 滑动窗口 /【模板】单调队列

「单调」指的是元素的「规律」——递增(或递减)。

「队列」指的是元素只能从队头和队尾进行操作。

要求的是每连续的 k k k 个数中的最大(最小)值,很明显,当一个数进入所要 “寻找” 最大值的范围中时,若这个数比其前面(先进队)的数要大,显然,前面的数会比这个数先出队且不再可能是最大值。

也就是说——当满足以上条件时,可将前面的数 “弹出”,再将该数真正 p u s h push push 进队尾。

这就相当于维护了一个递减的队列,符合单调队列的定义,减少了重复的比较次数,不仅如此,由于维护出的队伍是查询范围内的且是递减的,队头必定是该查询区域内的最大值,因此输出时只需输出队头即可。

显而易见的是,在这样的算法中,每个数只要进队与出队各一次,因此时间复杂度被降到了 O ( N ) O(N) O(N)

#include <iostream>
#include <cstdio>
#include <deque>

using namespace std;

const int maxn = 1e6 + 5;
int n, m;
int a[maxn];
deque<int> q;

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++)
    {
        while (!q.empty() && a[q.back()] > a[i])
            q.pop_back();
        q.push_back(i);
        if (i >= m)
        {
            while (!q.empty() && q.front() <= i - m)
                q.pop_front();
            printf("%d ", a[q.front()]);
        }
    }
    printf("\n");
    while (!q.empty())
        q.pop_back();
    for (int i = 1; i <= n; i++)
    {
        while (!q.empty() && a[q.back()] < a[i])
            q.pop_back();
        q.push_back(i);
        if(i>=m)
        {
            while(!q.empty()&&q.front()<=i-m) q.pop_front();
            printf("%d ",a[q.front()]);
        }
    }
    return 0;
}

单调队列应用场景:

单调队列(Monotonic Queue)是一种数据结构,常用于解决一些与滑动窗口相关的问题。它可以在 O ( 1 ) O(1) O(1) 的时间复杂度内实现以下操作:

  1. 在队尾添加元素
  2. 在队头移除元素
  3. 获取当前队列中的最大(或最小)元素

使用单调队列的典型场景包括:

  1. 滑动窗口最大值(或最小值)问题:给定一个数组和窗口的大小,需要找到每个窗口内的最大(或最小)值。单调队列可以帮助我们在窗口滑动的过程中高效地获取最大(或最小)值。

  2. 求解滑动窗口的某种性质:有时候我们需要在滑动窗口中求解的不仅仅是最大(或最小)值,还可能是其他一些特定的性质,比如窗口内的元素之和、平均值等。通过维护一个适当的单调队列,我们可以在滑动窗口中高效地求解这些性质。

  3. 解决一些需要在固定大小的窗口中维护最大(或最小)值的问题:比如在一个数据流中,需要不断更新当前窗口的最大(或最小)值。单调队列可以帮助我们在数据流中高效地维护这些值。

P1440 求m区间内的最小值

单调队列板子题,使用单调队列时需注意边界处理情况。手玩一遍即可。

P1714 切蛋糕

P10893 城市化发展委员会

Pending... \color{grey}\text{Pending...} Pending...

P10878 [JRKSJ R9] 在相思树下 III

Pending... \color{grey}\text{Pending...} Pending...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值