单调就是指一直增或一直减,例如1,2,3,4就是单调增,4,3,2,1就是单调减。而单调队列就有这种性质。
数据结构队列是先进先出,相当于数列3,7,5,9,现在有一个数2要进队,那么,按队列的操作应该将数列变为7,5,9,2。
然而单调队列就不一样。假设有一条队列7,6,3,4,现在又有一个数5要进队,却看见3,4都比5要弱~~(类似MYJ在某STAR中将LYH打爆的行为)~~,所以就有了一种类似插队的方法,5将3和4挤掉,来到了6的后面,构成了这种队列:
所以单调队列维护单调就是通过插队把队尾破坏了单调的数全部从队尾退队,使队列元素维持单调(单调队列与队列不同就是单调队列既可以从队首出队,也可以从队尾出队)。
看看一道模板水题
P1886 滑动窗口
据说可以用线段树,树状数组什么的,但单调队列是拯救蒟蒻的做法(逃
所以说,这道题到底怎么做?
其实只需要枚举所有的连续子序列,使用单调队列找出最大值。
开始乱搞写代码:
//我用的手写单调队列,但你要用STL的deque我也拦不住
#include<bits/stdc++.h>
using namespace std;
int n,k;
int a[1000001]={0},q[10000001]={0};q数组表示单调队列,a表示其所对应的在原列表里的下标
int head=1;//头指针
int tail=1;//队尾指针
int main(){
cin >> n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
while(head<tail&&a[q[tail-1]]>a[i]){
tail--;//插队,将后面的数踢掉,以维护单调性
}
q[tail++]=i;//存编号
while(head<tail&&q[head]<=i-k){
head++;//队首元素太old了,就出队。
}
if(i>=k){
cout <<a[q[head]]<< " ";//按题目要求的输出
}
}
cout << endl;
--------------------------------------------------------------------------------------------
head=1,tail=1;
for(int i=1;i<=n;i++){
while(head<tail&&a[q[tail-1]]<a[i]){//队列中存在元素,又队尾元素比下一个处理的值大,即表示队尾元素太old,就让队尾元素出队。当队尾元素小于下一个处理值,单调性成立。
tail--;
}
q[tail++]=i;//存编号
while(head<tail&&q[head]<=i-k){
head++;//队首元素太old了,就出队。
}
if(i>=k){
cout << a[q[head]] << " ";//cout队首元素,即最大值
}
}
cout << endl;
return 0;
//实际上分割线上下代码都差不多,只是维护单调性的while循环不一样
}