单调递增队列(全过程图文实现 另附习题)

什么是单调队列,有什么用?

不妨用一个问题来说明单调队列的作用和操作:
不断地向缓存数组里读入元素,也不时地去掉最老的元素,不定期的询问当前缓存数组里的最小的元素。
最直接的方法:普通队列实现缓存数组。
进队出队都是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;
} 

希望本文能让你有所收获……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章