博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【数据结构】单调数据结构之一:单调队列
阅读量:5277 次
发布时间:2019-06-14

本文共 2686 字,大约阅读时间需要 8 分钟。

零.前言

很明显的,本题是单调队列的模板题目。

为了方便我(和来赏光的 \(julao\) )今后复习,写一篇题解。

例题:

壹.简述

单调队列是一种数据结构(不知道算不算基础数据结构

它满足一下性质:

  1. 单调队列中的数是满足单调递增/递减/或是其他的性质
  2. 单调队列中的数的下标是单调递增的(这也是下面的几位 \(julao\) 将单调队列与优先队列混为一谈的错误之处了,优先队列会将下标改变)

贰.对于本题的分析

本题很明显是使用单调队列求窗口中得最大值与最小值得。

什么是窗口呢?

(虽然我只是口胡。。。反正窗口挺好理解的吧,,口胡这么多大家别绕晕了,就是你理解的那个窗口)

窗口是指:在一段序列中,从下标为 \(i\) 到下标为 \(i + k - 1\) 的下标连续的子序列。其中 \(k\) 为窗口大小。滑动窗口是指 \(i\) 会滑动,也就是递增或递减,从而使得整个子序列的长度不变,但涵盖的数变化了。

形象的讲,就是在一段子序列右端点随着左端点的增大而增大。

叁.对于单调队列的分析

单调队列有两个操作:删头和去尾(这两的名字是来源于)

(为了方便理解,本题当中的头是指最后进来的数所在的位置,尾为最先进来的数所在的位置)

一 去尾

很明显,本题的是有窗口的,也就是说,我们的单调队列中的数的下标,不仅要递增,而且不能过期。

所以每次我们更新队头的时候,都要判断队尾的下标是否过期了。

只要队尾的下标过期,即在以当前更新到的数为头的窗口外,就 \(pop\) 出去。

while (q.back().n <= i - k) q.pop_back(); // i 为当前的队头的下标, k 为窗口大小

(本题的单调队列用结构体来写的,结构体中的 \(m\) 代表值, \(n\) 代表下标)

二 删头

对于序列当中的每一个数都会更新一次。刚刚的去尾是维护单调队列下标的性质,现在我们来维护一下单调队列的递增性。

定义队头的数为 \(x\),更新到的数为 \(y\)

我们要维护一个单调递增的队列,也就是说对于队列中的所有数,下标比它大的值也比它大。

我们要维护一个窗口中最小值,要用单调递增的队列,因为队尾是最小的值。

第一种情况: \(y > x\)

​ 那就直接放入,因为这样不破坏数的单调性,放入这个数后队列依然是递增的

第二种情况: \(y < x\)

首先肯定不能直接放入,因为放入后单调性就会被破坏。首先,这个数比当前队头的数的下标要大,值却比它小。所以在 $x$ 的有生之年内,都不可能是窗口最小(或满足所求)的。因为我们已经更新到了 $y$,所以只要包括 $x$ 的窗口一定包括了 $y$,而且 $x > y$,所以一直到 $x$ 过期,他都不可能是最小的数。也就是说,一个数比你晚进来(下标比你大)还比你更满足我们要求的数的性质,那你就打不过他了。打不过怎么办,直接pop掉啊(好残忍)

上代码

while (!q.empty() && a[i].m <= q.front().m) q.pop_front(); // a为给出的序列,m和n的意义如上

所以经过我们刚才的分析,单调队列的通用模板如下:

while (!q.empty() && a[i].m 更符合所求的要求 q.front().m) q.pop_front();q.push_front(a[i]); // 现在的队头打得过更新到的数了while (q.back().n <= i - k) q.pop_back();printf("%d", q.back().m); // 队尾的是当前最符合要求的数,为什么呢?看下面

三 性质

刚才的两个操作分析完了,我们来分析一下那个是最符合性质的数。

每一个数都比后一个数更符合性质

所以就是第一个数了。

四 完整代码

#include 
using namespace std;const int maxn = 1e6 + 10;int all, k;struct node { int n, m; }a[maxn];deque
q;void BIG(int all, int k) { for (int i = 1; i <= all; ++i) { while (!q.empty() && a[i].m >= q.front().m )q.pop_front(); // 求最小的 q.push_front(a[i]); while (q.back().n <= i - k) q.pop_back(); if (i >= k) printf("%d ", q.back().m); } q.clear();}void LOW(int all, int k) { for (int i = 1; i <= all; ++i) { while (!q.empty() && a[i].m <= q.front().m) q.pop_front(); // 求最大的 q.push_front(a[i]); while (q.back().n <= i - k) q.pop_back(); if (i >= k) printf("%d ", q.back().m); } q.clear();}int main() { scanf("%d %d", &all, &k); for (int i = 1; i <= all; ++i) scanf("%d", &a[i].m), a[i].n = i; LOW(all, k); puts(""); BIG(all, k); return 0;}

代码部分参考了楼下 \(julao\) 的代码,有点抄题解的嫌疑

(题解的事情,能叫抄题解吗)

4.茶余饭后

有一天,有一个叫

\(julao\) 进入了一个叫“国家队”的单调队列。

然后,单调队列中所有的数除了 都被清空了。

包括我这个 \(juruo\)

很久以后,人们才敢回想起被小光支配的恐惧。

~~qwq~

如果你觉得这篇文章对你有所帮助,欢迎点击推荐!

转载于:https://www.cnblogs.com/blackwhitetony/p/10350496.html

你可能感兴趣的文章
11)Java abstract class 和 interface
查看>>
使用xrdp或Xmanager 远程连接 CentOS6
查看>>
Linux误删恢复
查看>>
Unity调用Windows窗口句柄,选择文件和目录
查看>>
HashMap循环遍历方式
查看>>
React Native 入门 调试项目
查看>>
C# 通过 Quartz .NET 实现 schedule job 的处理
查看>>
关于java之socket输入流输出流可否放在不同的线程里进行处理
查看>>
目前为止用过的最好的Json互转工具类ConvertJson
查看>>
Day13
查看>>
tensorflow saver简介+Demo with linear-model
查看>>
Luogu_4103 [HEOI2014]大工程
查看>>
Oracle——SQL基础
查看>>
项目置顶随笔
查看>>
Redis的安装与使用
查看>>
P1970 花匠
查看>>
java语言与java技术
查看>>
NOIP2016提高A组五校联考2总结
查看>>
iOS 项目的编译速度提高
查看>>
table中checkbox选择多行
查看>>