单调队列,顾名思义,就是一个元素具有单调性的队列,{1,2,5}这是一个单调增的队列,{6,4,1}这是一个单调减的队列。
其用途我们通过一道例题来说明
洛谷P1866 滑动窗口
现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
例如

题意是有一个长度为N的序列,还有一个长度为k的区间,区间每次向右滑动一位,求每次滑动过后的最大值和最小值。
这道题可以用线段树或ST表来解决,这里仅仅说单调队列。
如果我们维护一个队列,保持队首是要查找的值,那么每次滑动的时候,我们输出队首即可。
现在问题是,我们怎么样维护这样一个合法的队首,我们就应用了单调队列。
这里以最大值为例,试想,如果碰到一个新的元素num[j](i<j),如果num[i]>num[j],那么num[i]和num[j]都是有用的,
因为如果窗口往后滑,num[i]滑出了区间,弹出队列,那么num[j]可能会成为那个合法的队首,所以num[i]和num[j],
我们都要记下来,再如果num[i]<=num[j](i<j),那么num[i]永远也不会成为将要查询区间的合法元素,所以直接弹掉
即可,明确了这一点剩下只是一些简单的操作。
在队列中记下每个元素在原序列中的位置,如果这个元素不在查询序列中了就弹掉。
如果碰到num[i]<=num[j](i<j),就弹掉队尾破坏单调性的元素,知道满足单调性了,就将新的元素加入。
如果碰到num[i]>num[j] (i<j),就把num[j]加入队尾,因为没有破坏单调性。
最小值的查询相反即可。
P1866 代码(维护一个单调增队列查询最小值,再维护一个单调减队列查询最大值)
1 #include<cstdio>
2 #include<iostream>
3 #include<cstring>
4 #include<algorithm>
5 #define maxn 1000005
6
7 using namespace std;
8
9 struct node
10 {
11 int data,pos;
12 };
13 node q[maxn];
14 int num[maxn],n,k;
15
16 int main()
17 {
18 scanf("%d%d",&n,&k);
19 for(int i=1;i<=n;i++)
20 scanf("%d",&num[i]);
21 int head=1,tail=0;
22 for(int i=1;i<=n;i++)
23 {
24 while(head<=tail&&q[head].pos+k<=i) head++;
25 while(head<=tail&&q[tail].data>=num[i]) tail--;
26 q[++tail].data=num[i]; q[tail].pos=i;
27 if(i>=k) printf("%d ",q[head].data);
28 }
29 printf("\n");
30 head=1; tail=0;
31 for(int i=1;i<=n;i++)
32 {
33 while(head<=tail&&q[head].pos+k<=i) head++;
34 while(head<=tail&&q[tail].data<=num[i]) tail--;
35 q[++tail].data=num[i]; q[tail].pos=i;
36 if(i>=k) printf("%d ",q[head].data);
37 }
38 return 0;
39 }