单调队列
单调队列
顾名思义,单调队列就是具有单调性的队列,我们需要用双端队列来实现。
双端队列与普通队列的唯一区别就是它可以从两边弹出。
打个形象一点儿的比方
一群人在食堂排队打饭
这个时候来了一个新的小伙伴
这个小伙伴很霸道地想要去插队,其他小伙伴们肯定不会同意,但是打不过这个新伙伴,于是就被赶走了。
但是呢,它又打不过最前面的那个6,于是就只有乖乖地站在它后面
过了一会儿,6打完了饭离开了队列,而5还在队列中继续打饭
以上大概就是单调队列的操作规则
单调队列的应用
百度上对单调队列的解释是:
不断地向缓存数组里读入元素,也不时地去掉最老的元素,不定期的询问当前缓存数组里的最小的元素。
由于单调队列具有单调性,而且有淘汰旧元素的规则,我们通常用它来维护某个特定范围内的最值。
举几个经典的例子:
滑动窗口
luogu P1886
非常明显的满足单调队列的性质
用两个单调队列分别维护最大值和最小值。
每滑动一次就要考虑两件事情
- 淘汰掉虽然很优秀(很大or小)但是已经不在范围内的元素 (从队头)
- 淘汰掉不够优秀的元素(即队列中比当前元素更(小or大)的元素,由于它们出现在当前元素之前,又没有当前元素优秀,所以当窗口继续滑动时,它们有生之年都不可能成为最值了,只能成为时代的眼泪)
(从队尾)
#include<cstdio>
#include<deque>
using namespace std;
#define MAXN 1000005
int n,k;
int a[MAXN],ans[MAXN];
deque<int>Q;//维护在原数组中的编号
void Min()
{
int j;
for(int i=1;i<=n;i++)
{
while(!Q.empty())//维护队尾最小值
{
j=Q.back();
if(a[j]>=a[i])
Q.pop_back();
else break;
}
Q.push_back(i);
while(!Q.empty())
{
j=Q.front();
if(i-j>=k)
Q.pop_front();
else break;
}
ans[i]=a[Q.front()];
}
for(int i=k;i<n;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
void Max()
{
Q.clear();//注意清零
int j;
for(int i=1;i<=n;i++)
{
while(!Q.empty())
{
j=Q.back();
if(a[j]<=a[i])
Q.pop_back();
else break;
}
Q.push_back(i);
while(!Q.empty())
{
j=Q.front();
if(i-j>=k)
Q.pop_front();
else break;
}
ans[i]=a[Q.front()];
}
for(int i=k;i<n;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
Min();
Max();
return 0;
}
单调队列与优先队列
网上看到的,比较有道理:
单调队列与优先队列的区别:单调队列的长度取决于输入数据的合法性,而优先队列的长度始终与输入数据的数量等同。而他们的单调性都是单调递减或单调递增。
而我自己的理解,就是:
他们同样都维护了一个单调性
但是优先队列对数据照盘全收,而单调队列有淘汰旧元素的过程。所以你不好好努力,不够优秀的话,是没有办法在单调队列里活下来的
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com