D 滑動視窗 (單調佇列)

2021-10-04 06:31:30 字數 2086 閱讀 8361

zjm 有乙個長度為 n 的數列和乙個大小為 k 的視窗, 視窗可以在數列上來回移動. 現在 zjm 想知道在視窗從左往右滑的時候,每次視窗內數的最大值和最小值分別是多少. 例如:

數列是 [1 3 -1 -3 5 3 6 7], 其中 k 等於 3.

window position

minimum value

maximum value

[1 3 -1] -3 5 3 6 7-13

1 [3 -1 -3] 5 3 6 7-33

1 3 [-1 -3 5] 3 6 7-35

1 3 -1 [-3 5 3] 6 7-35

1 3 -1 -3 [5 3 6] 736

1 3 -1 -3 5 [3 6 7]37

input

輸入有兩行。第一行兩個整數n和k分別表示數列的長度和滑動視窗的大小,1<=k<=n<=1000000。

第二行有n個整數表示zjm的數列。

output

輸出有兩行。第一行輸出滑動視窗在從左到右的每個位置時,滑動視窗中的最小值。第二行是最大值。
sample input

8 3

1 3 -1 -3 5 3 6 7

sample output

-1 -3 -3 -3 3 3

3 3 5 5 6 7

本題核心演算法為單調佇列。

單調佇列與單調棧區別在於,單調棧因為只有棧頂可以操作,所以儲存的內容總是全域性的,而單調佇列兩邊都可以操作,所以儲存的內容可以是區域性的。所以單調佇列可以在這裡用於處理乙個滑動視窗。

兩次遍歷整個陣列,一次遞增佇列,一次遞減佇列,分別計算滑動視窗內最小值、最大值。

(遞增佇列中隊首元素總是滑動視窗內最小值,遞減佇列中隊首元素總是滑動視窗內最大值)

如何維護乙個單調佇列呢?(以單增隊列為例)

遍歷整個陣列:

(1)判斷隊首元素是否仍然屬於滑動視窗。若不屬於,隊首元素出佇列。

(2)判斷目前陣列元素是否可以入佇列。佇列未滿且該元素大於等於隊尾元素,則可以入佇列。否則,將隊尾元素出佇列,直到隊列為空或隊尾元素小於等於該元素,再將該元素入佇列。

維護單調佇列的同時,在恰當位置輸出每個滑動視窗最小值、最大值,即為正確答案。

#include

using

namespace std;

int n=

0,k=0;

int a[

10000000];

int b[

10000000];

//佇列

intmain()

//輸入

//最小值

int front=

0,tail=-1

;for

(int i=

0;i)while

(tail>=front&&a[b[tail]

]>=a[i]

)//入佇列

b[++tail]

=i;if

(i>=k-1)

}printf

("\n");

//最大值

front=

0; tail=-1

;for

(int i=

0;i)while

(tail>=front&&a[b[tail]

]<=a[i]

)//入佇列

b[++tail]

=i;if

(i>=k-1)

}printf

("\n");

return0;

}

輸出的位置一定要確定好,在視窗滑動過程中就找到合適位置輸出,就不必再儲存,浪費記憶體空間了。

雖然是維護兩個單調佇列,但是兩個佇列先後出現,可以指宣告乙個b陣列,反覆使用,通過對引數的控制對其反覆使用,節省空間。

因為需要判斷隊首元素是否還在滑動視窗內,所以單調佇列中必須儲存元素的編號,而非編號的值!

單調佇列 滑動視窗

nkoj 2152 description 給你乙個長度為n n 10 6 的陣列,乙個長為k的滑動的窗體從最左移至最右端,你只能見到視窗的k個數,每次窗體向右移動一位,找出窗體所包含的數字的最大和最小值,如下表所示 k的值為3 視窗位置 最小值 最大值 1 3 1 3 5 3 6 7 1 3 1 ...

滑動視窗 單調佇列

給定乙個大小為n 106的陣列。有乙個大小為k的滑動視窗,它從陣列的最左邊移動到最右邊。您只能在視窗中看到k個數字。每次滑動視窗向右移動乙個位置。以下是乙個例子 該陣列為 1 3 1 3 5 3 6 7 k為3。視窗位置 最小值 最大值 1 3 1 3 5 3 6 7 1 3 1 3 1 3 5 3...

單調佇列 滑動視窗

p1886 滑動視窗 模板 單調佇列 有乙個長為 n 的序列 a,以及乙個大小為 k 的視窗。現在這個從左邊開始向右滑動,每次滑動乙個單位,求出每次滑動後視窗中的最大值和最小值。如果按照暴力方法做的話,每一次判斷相鄰的k個數的最大值和最小值,複雜度為o n k 肯定會超時,因此就想到把每次的最大值和...