Sliding Window
Time Limit: 12000MS Memory Limit: 65536K
Total Submissions: 56028 Accepted: 16112
Case Time Limit: 5000MSnode
Window position | Minimum value | Maximum value |
---|---|---|
[1 3 -1] -3 5 3 6 7 | -1 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5] 3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 3 | 7 |
Your task is to determine the maximum and minimum values in the sliding window at each position. ios
Input數組
Output數據結構
Sample Inputspa
8 3 1 3 -1 -3 5 3 6 7
Sample Outputcode
-1 -3 -3 -3 3 3 3 3 5 5 6 7
題意: 給定一行數,共N個。有一個長度爲K的窗口從左向右滑動,窗口中始終有K個數字,窗口每次滑動一個數字。求各個時刻窗口中的最大值和最小值。blog
1、概念介紹
一、 雙端隊列
雙端隊列是一種線性表,是一種特殊的隊列,遵照先進先出的原則。雙端隊列支持如下4種操做:隊列
(1) 從隊首刪除
(2) 從隊尾刪除
(3) 從隊尾插入
(4) 查詢線性表中任意一元素的值
二、 單調隊列
單調隊列是一種特殊的雙端隊列,其內部元素具備單調性。最大隊列與最小隊列是兩種比較經常使用的單調隊列,其內部元素分別是嚴格單調遞減(不是非遞增)和嚴格單調遞增(不是非遞減)的。get
單調隊列的經常使用操做以下:input
(1) 插入:若新元素從隊尾插入後會破壞單調性,則刪除隊尾元素,直到插入後再也不破壞單調性爲止,再將其插入單調隊列。
(2) 獲取最優(最大、最小)值:訪問隊首元素
如下是一個單調遞增隊列的例子:
隊列大小不能超過3,入隊元素依次爲3,2,8,4,5,7,6,4
3入隊:(3)
3從隊尾出隊,2入隊:(2)
8入隊:(2,8)
8從隊尾出隊,4入隊:(2,4)
5入隊:(2,4,5)
2從隊頭出隊,7入隊:(4,5,7)
7從隊尾出隊,6入隊:(4,5,6)
6從隊尾出隊,5從隊尾出隊,4從隊尾出隊,4入隊:(4)
以上左端爲隊頭,右端爲隊尾。
2、單調隊列的應用
一、最大值的維護:
好比咱們要維護一個區間爲k的最大值的單調隊列,因爲新插入 的節點他的「生命力」確定比原先已經在隊列中的元素「活」的時間長,將插入元素不斷與隊尾元素比, 若是他大於隊尾元素,那麼tail--將隊尾元素刪掉,(由於目前插入的這個元素值(設爲pos)更大,並且「活」的時間 長,有pos在,隊尾元素的有「生」之年永遠都無法爲最大值,故而直接無視比pos小的隊尾了)。直到對空位置或者 找到了一個比pos大的隊尾。
這個問題至關於一個數據流(數列a)在不斷地到來,而數據是不斷過時的,至關於咱們只能保存有限的數據(sliding window中的數據,此題中就是窗口的寬度w),對於到來的查詢(此題中查詢是每時刻都有的),咱們要返回當前滑動窗口中的最大值最小值。注意,元素是不斷過時的。
解決這個問題可使用一種叫作單調隊列的數據結構,它維護這樣一種隊列:
a)從隊頭到隊尾,元素在咱們所關注的指標下是遞減的(嚴格遞減,而不是非遞增),好比查詢若是每次問的是窗口內的最小值,那麼隊列中元素從左至右就應該遞增,若是每次問的是窗口內的最大值,則應該遞減,依此類推。這是爲了保證每次查詢只須要取隊頭元素。
b)從隊頭到隊尾,元素對應的時刻(此題中是該元素在數列a中的下標)是遞增的,但不要求連續,這是爲了保證最左面的元素老是最早過時,且每當有新元素來臨的時候必定是插入隊尾。
知足以上兩點的隊列就是單調隊列,首先,只有第一個元素的序列必定是單調隊列。
那麼怎麼維護這個單調隊列呢?無非是處理插入和查詢兩個操做。
對於插入,因爲性質b,所以來的新元素插入到隊列的最後就能維持b)繼續成立。可是爲了維護a)的成立,即元素在咱們關注的指標下遞減,從隊尾插入新元素的時候可能要刪除隊尾的一些元素,具體說來就是,找到第一個大於(在所關注指標下)新元素的元素,刪除其後全部元素,並將新元素插於其後。由於全部被刪除的元素都比新元素要小,並且比新元素要舊,所以在之後的任何查詢中都不可能成爲答案,因此能夠放心刪除。
對於查詢,因爲性質b,所以全部該時刻過時的元素必定都集中在隊頭,所以利用查詢的時機刪除隊頭全部過時的元素,在不含過時元素後,隊頭得元素就是查詢的答案(性質a),將其返回便可。因爲每一個元素都進隊出隊一次,所以攤銷複雜度爲O(n)。
模板:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 const int maxn = 1e6 + 5; 7 struct node 8 { 9 int pos, val; 10 }q[maxn]; 11 int k, num[maxn], n, Min[maxn], Max[maxn]; 12 void getmin() 13 { 14 int head = 1, tail = 0; 15 for(int i = 1; i < k; i++) 16 { 17 while(head <= tail && num[i] < q[tail].val) tail--; 18 tail++; 19 q[tail].val = num[i]; 20 q[tail].pos = i; 21 } 22 for(int i = k; i <= n; i++) 23 { 24 while(head <= tail && num[i] < q[tail].val) tail--; 25 tail++; 26 q[tail].val = num[i]; 27 q[tail].pos = i; 28 while(i-q[head].pos >= k) head++; 29 Min[i-k] = q[head].val; 30 } 31 } 32 void getmax() 33 { 34 int head = 1, tail = 0; 35 for(int i = 1; i < k; i++) 36 { 37 while(head <= tail && num[i] > q[tail].val) tail--; 38 tail++; 39 q[tail].val = num[i]; 40 q[tail].pos = i; 41 } 42 for(int i = k; i <= n; i++) 43 { 44 while(head <= tail && num[i] > q[tail].val) tail--; 45 tail++; 46 q[tail].val = num[i]; 47 q[tail].pos = i; 48 while(i - q[head].pos >= k) head++; 49 Max[i-k] = q[head].val; 50 } 51 52 } 53 int main() 54 { 55 while(~scanf("%d%d", &n, &k)) 56 { 57 for(int i = 1; i <= n; i++) scanf("%d", &num[i]); 58 getmin(); 59 for(int i=0; i<=n-k; i++) printf("%d%c",Min[i],i==n-k?'\n':' '); 60 getmax(); 61 for(int i=0; i<=n-k; i++) printf("%d%c",Max[i],i==n-k?'\n':' '); 62 } 63 return 0; 64 }
用優先隊列寫的:操做的是下標
1 #include<algorithm> 2 #include<queue> 3 #include<vector> 4 using namespace std; 5 int a[1000011];//數組數據 6 7 int OutMin[1000011];//最小值 8 9 int OutMax[1000011];//最大值 10 11 int cnt1=0; 12 int cnt2=0; 13 int n,k; 14 15 struct cmp1 16 { 17 bool operator()(const int a1,const int a2) 18 { 19 return a[a1]>a[a2]; //這裏太精髓,重定義的是數組元素值,可是隊列存的是下標 20 } 21 }; 22 struct cmp2 23 { 24 bool operator()(const int a1,const int a2) 25 { 26 return a[a1]<a[a2]; 27 } 28 }; 29 priority_queue <int ,vector<int>,cmp1> Q1; //重定義符號 30 priority_queue <int ,vector<int>,cmp2> Q2; 31 int main() 32 { 33 int i; 34 scanf("%d%d",&n,&k); 35 if(k>n) 36 k=n; 37 for(i=1;i<=n;++i) 38 { 39 scanf("%d",&a[i]); 40 } 41 for(i=1;i<=k;++i) 42 { 43 Q1.push(i); 44 Q2.push(i); 45 } 46 OutMin[cnt1++]=a[Q1.top()]; 47 OutMax[cnt2++]=a[Q2.top()]; 48 for(i=k+1;i<=n;++i) 49 { 50 Q1.push(i); 51 Q2.push(i); 52 while(i-Q1.top()>=k) 53 Q1.pop(); 54 OutMin[cnt1++]=a[Q1.top()]; 55 while(i-Q2.top()>=k) 56 Q2.pop(); 57 OutMax[cnt2++]=a[Q2.top()]; 58 } 59 60 for(i=0;i<=(n-k);++i) 61 { 62 printf("%d%c", OutMin[i], (i < n - k) ? ' ' : '\n'); 63 64 } 65 for(i=0;i<=(n-k);++i) 66 { 67 printf("%d%c", OutMax[i], (i < n - k) ? ' ' : '\n'); 68 } 69 return 0; 70 }