单调队列

Posted mr94kevin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单调队列相关的知识,希望对你有一定的参考价值。

单调队列是指在任意时刻,队列中的元素都是单调的(递增或递减),同时他又具有双端队列的部分性质(允许从队尾删除元素)。

在这里,有一道经典的例题:滑动窗口求最值

题目描述:在一个长度为n的整数序列上有一个长度为k的滑动窗口,求滑动窗口内的最(大/小)值。

解释:就是在一个序列上对于每个长度为k的区间,求区间内的最值。

分析:一种朴素的做法是,枚举区间起点,再自此向后比较k个元素,找出最值,这样的复杂度是O(nk)的。

还有一种不错的做法是利用单调队列。不妨假设我们已经得到了一个单调队列,他维护了当前的滑动窗口,显然,

队首元素就是窗口内的最值。现在再来考虑如何用单调队列维护滑动窗口(以最大值为例,队列则为单调递减的,队首元素为窗口内的最大值):

我们遍历序列的每个元素,当队列为空时,肯定要加入队列;队列不为空,就要加入队列,从队尾弹出较小的元素,再加入,保证队列单调;

但滑动窗口是有长度限制的,怎么考虑呢?我们可以保存每个元素的序号,当发现队首元素的序号与当前考虑元素相比,已经出了滑动窗口,就弹出队首元素。

技术分享图片
 1 #include<cstdio>
 2 #include<iostream>
 3 #include<deque>
 4 using namespace std;
 5 struct num { //定义结构体存储元素的序号及值
 6     int id,value;
 7     num() {}
 8     num(int i,int v):id(i),value(v) {}
 9 };
10 int n,k,a[10005],first=1; //first用于限制输出格式
11 deque<num> dq;
12 int main() {
13     cin>>n>>k;
14     for(int i=1;i<=n;++i) { //边读边处理
15         scanf("%d",&a[i]); //数据规模略大,建议不要用cin
16         //也可以用读入优化,输出那里同理
17         if(dq.empty()) dq.push_back(num(i,a[i]));
18         //若队列为空,则直接进入队列
19         else {
20             num f=dq.front(); //取队首元素
21             if(i>f.id+k-1) dq.pop_front();
22             //若队首元素的序号距当前元素太远(超出窗口长度)则弹出队首元素
23             num b=dq.back(); //取队尾元素
24             while(b.value<a[i]) { //若队尾元素小于当前元素则弹出队尾
25                 dq.pop_back();
26                 if(dq.empty()) break; //注意!队列为空则不能继续弹出
27                 b=dq.back();
28             }
29             dq.push_back(num(i,a[i])); //将当前元素放入队列中合适位置
30         }
31         if(i>=k) { //当考虑的元素个数达到窗口长度时,开始输出
32             if(first) first=0; //控制输出格式
33             else printf("
");
34             num f=dq.front();
35             printf("第%d个滑动窗口的最大值为%d",i-k+1,f.value);
36         } //事实上,对于长度为n的序列,长度为k的窗口,共有n-k+1个不同的窗口
37     }
38     return 0;
39 }
code

 

以上是关于单调队列的主要内容,如果未能解决你的问题,请参考以下文章

HDU 6957 Maximal submatrix(悬线法 || 优先队列 || 单调栈 )

POJ 2823 Sliding Window 单调队列

单调队列与单调栈作用

单调队列题目练习

POJ 2823 Sliding Window(单调队列)

单调队列单调栈优先队列模板