单调队列学习笔记

前言

我们常说:“我又被单调队列了/kk”,意为“我比比自己小还比自己强的人吊打了”。

今天我们赖了解什么是“单调队列”。

分析

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

毫无疑问,最开始必定想到的是 O(nk) 的暴力。

代码不贴啦,必定 TLE

考虑优化。

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

  • 摘自 OI Wiki。

这是复杂度为 O(n)

对于样例分析

1 3 -1 -3 5 3 6 7

操作 队列
1 入队 {1}
3 入队 {1,3}
-1 入队,队内元素大于 -1 的出队 {-1}
-3 入队,队内元素大于 -3 的出队 {-3}
5 入队 {-3,5}
3 入队,队内元素大于 3 的出队 {-3,3}
6 入队,-3 不在窗内,出队。 {3,6}
7 入队 {3,6,7}

Code

  • 摘自 OI Wiki。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define maxn 1000100
using namespace std;
int q[maxn], a[maxn];
int n, k;
void getmin() {  //得到这个队列里的最小值,直接找到最后的就行了
  int head = 0, tail = 0;
  for (int i = 1; i < k; i++) {
    while (head <= tail && a[q[tail]] >= a[i]) tail--;
    q[++tail] = i;
  }
  for (int i = k; i <= n; i++) {
    while (head <= tail && a[q[tail]] >= a[i]) tail--;
    q[++tail] = i;
    while (q[head] <= i - k) head++;
    printf("%d ", a[q[head]]);
  }
}

void getmax() {  //和上面同理
  int head = 0, tail = 0;
  for (int i = 1; i < k; i++) {
    while (head <= tail && a[q[tail]] <= a[i]) tail--;
    q[++tail] = i;
  }
  for (int i = k; i <= n; i++) {
    while (head <= tail && a[q[tail]] <= a[i]) tail--;
    q[++tail] = i;
    while (q[head] <= i - k) head++;
    printf("%d ", a[q[head]]);
  }
}

int main() {
  scanf("%d%d", &n, &k);
  for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
  getmin();
  printf("\n");
  getmax();
  printf("\n");
  return 0;
}
//deque实现
#include <cstdio>
#include <queue> // 提供deque
#define MAXN 2000005
using namespace std;

struct Num{
    int index,x;//需要记录单调队列内每个数的入队时间(index)和大小(x)
};

int a[MAXN]; //原数组
deque<Num> q; //单调队列 

int main(void){
    int n,m; //n表示序列长度,m表示滑动窗口长度
    Num t;//保存当前元素
    //输入
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    //问题解决
    for (int i=1;i<=n;i++){
        //先输出数a[i]前的最小值
        if (q.empty()) //q空,即a[i]前没有元素
            printf("0\n");
        else { //否则判断队头是否需要出队并输出范围内的队头
            if (q.front().index+m<i) //队头已经超出滑动窗口范围
                q.pop_front(); // 弹出队头
            printf("%d\n",q.front().x); //此时队一定非空(想想为什么)
        }
        while ((!q.empty()) && q.back().x>=a[i]) 
        //当队列非空时,不断弹出队尾比当前元素大的元素
            q.pop_back();
        t.index=i;
        t.x=a[i];
        q.push_back(t);//将当前元素入队
        //注意:当前元素无论如何都会入队(想想为什么)
    }
    return 0;
}

无耻求赞求关注(((q(≧▽≦q)

本文作者:int32

本文链接:https://www.cnblogs.com/lycqwq/articles/mono-queue-note.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @ 2022-01-21 20:32  int32  阅读(46)  评论(0)    收藏  举报
编辑推荐:
· 从 Redis 客户端超时到 .NET 线程池挑战
· C23和C++26的#embed嵌入资源指南
· 「EF Core」框架是如何识别实体类的属性和主键的
· 独立开发,这条路可行吗?
· 我在厂里搞 wine 的日子
阅读排行:
· 他没买 iPad,而是花了半年时间,为所有“穷学生”写了个笔记神器
· Visual Studio 现已支持新的、更简洁的解决方案文件(slnx)格式
· 只需一行命令,Win11秒变Linux开发主机!
· 从 Redis 客户端超时到 .NET 线程池挑战:饥饿、窃取与阻塞的全景解析
· 2025年中总结:我想我克服公众演讲的恐惧了,一个社恐分子突破自我的故事
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起