Acwing_154 滑動窗口
題目鏈接:https://www.acwing.com/problem/content/156/
好久沒有做單調隊列的題目了,正好碰到了重新複習一波;
對這道題目而言;我們首先考慮樸素做法,直接暴力去遍歷每次拆窗口的數值,時間複雜度爲:0(n * k);
在暴力的過程中,我們發現;對於一個這樣一個窗口:
當-3,存在的時候,-1和3是永遠不可能是最小數的;可以得到一個這樣的結論,當一個數出現,如果說這個數比前面出現過的數小,那麼,在後面窗口不斷進行滑動的過程中,前面的數字就是無效的;所以這裏我們需要考慮維護兩個東西,一個是數字本身的單調性,一個是數字本身鎖出現的順序;
思路:當一個新數字出現的時候,我們首先需要判斷當前的隊頭元素是否已經不在當前的窗口長度中,如果不在則將其出隊;然後我們再對隊列剩下的數字進行判斷,並且此時從隊尾進行判斷,若隊列中的數大於當前即將進入的數字,那麼就將當前隊尾的數字出隊伍(此時是一個雙向隊列)直到隊列爲空或者找到當前隊尾的元素的數值比當前即將要進來的數字更小就停止出隊;最後將這個數字進隊;
貼上代碼:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e6 + 5;
int a[maxn];
int q[maxn], hh = 0, tt = -1; //hh:隊頭, tt:隊尾
int main(void) {
// freopen("in.txt", "r", stdin);
int n, k;
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
//輸出滑動窗口中的最小值
for(int i = 1; i <= n; i ++) {
//首先判斷對頭的元素是否需要出隊,由於窗口每滑動一次只會對一個元素進行操作,所以此處只需要if
if(hh <= tt && i - k + 1 > q[hh]) hh ++; //如果當前隊列中有元素並且當前窗口中初始的下標已經大於了此時隊頭元素的下標,則出隊
while(hh <= tt && a[q[tt]] >= a[i]) tt --; //如果當前隊尾的元素的大小大於了a[i],那麼直接將隊尾元素出隊即可;此時的單調隊可以看成是一個雙向隊列
q[++ tt] = i; //當前元素的下標入隊列
if(i >= k) printf("%d ", a[q[hh]]);
}
puts("");
//輸出滑動窗口中的最大值
hh = 0, tt = -1;
for(int i = 1; i <= n; i ++) {
if(hh <= tt && i - k + 1 > q[hh]) hh ++;
while(hh <= tt && a[q[tt]] <= a[i]) tt --;
q[++ tt] = i;
if(i >= k) printf("%d ", a[q[hh]]);
}
// fclose(stdin);
return 0;
}