什么是单调队列,有什么用?
不妨用一个问题来说明单调队列的作用和操作:
不断地向缓存数组里读入元素,也不时地去掉最老的元素,不定期的询问当前缓存数组里的最小的元素。
最直接的方法:普通队列实现缓存数组。
进队出队都是O(1),一次查询需要遍历当前队列的所有元素,故O(n)。
单调队列的运用
假设数列:7 8 1 1 4 3 2
现在我们要求出以3个元素为区间大小的条件下,每个区间的最小值。
初步判断:
第一个区间:7 8 1最小值为 1
第二个区间:8 1 1最小值为 1
第三个区间:1 1 4最小值为 1
第四个区间:1 4 3最小值为 1
第五个区间:4 3 2最小值为 2
单调队列如何实现?
以上面假设为例,我们要维护一个单调递增的队列。
从单调队列的定义我们知道,数列中的每个元素都要进队列,那么队列有进自然就有出,那出队又是怎么实现的呢?也许不应该称为出队,称为覆盖会更为恰当。
我们需要不断更新以下几点(变量定义):
①、已入队元素数量 i
②、队头元素在数列中的序号 start
③、队尾元素在数列中的序号 end
④、区间大小m
输出队头元素要满足三个条件:
①、该队列符合递增。
②、i - start < m。
③、i >= m 。
队列实现步骤
为了更清晰展现实现过程,下文将用变量来代替部分文字。
若对变量定义不理解的朋友,请看上方 变量定义 。
①、首元素入队:7
变量: i = 1, start = 1, m = 3
分析:
1、该队列符合递增,符合。
2、i - start < m,符合。
3、i < m ,不符合。
结果:不输出队头元素 7 。
②、入队:8
变量: i = 2, start = 1, m = 3
分析:
1、该队列符合递增,符合。
2、i - start < m,符合。
3、i < m ,不符合。
结果:不输出队头元素 7 。
③、入队:1
变量: i = 3, start = 1, m = 3
分析:
1、该队列不符合递增。
操作:舍弃队列中比第三个元素1大的元素7 8。
变量: i = 3, start = 3, m = 3
分析:
1、该队列符合递增,符合。
2、i - start < m,符合。
3、i >= m ,符合。
结果:输出队头元素 1 。
④、入队:1
变量: i = 4, start = 3, m = 3
分析:
1、该队列符合递增,符合。
2、i - start < m,符合。
3、i >= m ,符合。
结果:输出队头元素 1 。
⑤、入队:4
变量: i = 5, start = 3, m = 3
分析:
1、该队列符合递增,符合。
2、i - start < m,符合。
3、i >= m ,符合。
结果:输出队头元素 1 。
⑥、入队:3
变量: i = 6, start = 3, m = 3
分析:
1、该队列不符合递增。
操作:舍弃队列中比第六个元素3大的元素 4 。
变量: i = 6, start = 3, m = 3
分析:
1、该队列符合递增,符合。
2、i - start == m ,不符合。
变量: i = 6, start = 4, m = 3
分析:
1、该队列符合递增,符合。
2、i - start < m,符合。
3、i >= m ,符合。
结果:输出队头元素 1 。
⑦、入队:2
变量: i = 7, start = 4, m = 3
分析:
1、该队列不符合递增。
操作:舍弃队列中比第七个元素2大的元素 3 。
变量: i = 7, start = 4, m = 3
分析:
1、该队列符合递增,符合。
2、i - start < m,不符合。
变量: i = 7, start = 5, m = 3
分析:
1、该队列符合递增,符合。
2、i - start < m,符合。
3、i >= m ,符合。
结果:输出队头元素 2 。
最终输出:1 1 1 1 2
与初步判断输出相同!
完成!
习题
题目描述
一个含有n项的数列(n<=2000000),求出每一项前的m个数到它这个区间内的最小值。若前面的数不足m项则从第1个数开始,若前面没有数则输出0。
输入格式
第一行两个数n,m。
第二行,n个正整数,为所给定的数列。
输出格式
n行,第i行的一个数ai,为所求序列中第i个数前m个数的最小值。
输入输出样例
输入
6 2
7 8 1 4 3 2
输出
0
7
7
1
1
3
说明/提示
【数据规模】
m ≤ n ≤ 2000000
ai ≤ 3×107
洛谷测试平台
运用单调递增队列的思想来完成本题。
①、创建结构数组,保存每项数列的数据及排列顺序。
//维护一个单调递增的动态队列:start~end
struct node{
int x; //数据大小
int y; //排列顺序
}q[2000010];
②、不断维护一个单调递增的动态队列。
注意:队列长度不定,有效的部分q[start] ~ q[end]。
for(int i=1; i<n; i++){
//start 元素不符合条件,该点已经不在查找的区间内
//end 始终不会大于 start+m
if(i-q[start].y>m){
start++;
}
printf("%d\n", q[start].x);
int tmp;
scanf("%d", &tmp);
//将 tmp 插入队列 start~end 中
while(end>start && q[end-1].x>tmp){
end--;
}
q[end].x=tmp;//覆盖原本q[end]
q[end].y=i;
end++;
}
整合代码
#include<stdio.h>
struct node{
int x;
int y;
}q[2000010];
int main(){
int n, m, start=0, end=1;
scanf("%d%d", &n, &m);
scanf("%d", &q[0].x); //先插入队列一个元素
//防止队列为空
q[0].y=0;
printf("0\n");
for(int i=1; i<n; i++){
if(i-q[start].y>m){
start++;
}
printf("%d\n", q[start].x);
int tmp;
scanf("%d", &tmp);
while(end>start && q[end-1].x>tmp){
end--;
}
q[end].x=tmp;
q[end].y=i;
end++;
}
return 0;
}
希望本文能让你有所收获……