單調遞增隊列(全過程圖文實現 另附習題)

什麼是單調隊列,有什麼用?

不妨用一個問題來說明單調隊列的作用和操作:
不斷地向緩存數組裏讀入元素,也不時地去掉最老的元素,不定期的詢問當前緩存數組裏的最小的元素。
最直接的方法:普通隊列實現緩存數組。
進隊出隊都是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;
} 

希望本文能讓你有所收穫……

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