单调队列板子:求滑动窗口中最大值和最小值
文章目錄
- 題目分析
- 初始思路
- 單調隊列優化的思路
- 代碼1:數組模擬單調隊列的代碼
- 代碼2:deque容器實現
能用到單調隊列的情景比較有限: 1.典型的有滑動窗口的最值, 2.找到里它最近的比它大(小)的元素。
題目大意:滑動窗口求最值:您的任務是確定滑動窗口位于每個位置時,窗口中的最大值和最小值。
題目分析
初始思路
如果不考慮單調隊列優化,普通的做法我們如何思考的呢?
首先是實現窗口的滑動: 維護一個隊列,新元素插入到隊頭,隊尾元素刪除掉,實現滑動窗口右移一位。 n次操作
其次是求窗口內的最值: 遍歷一遍 k次操作
樸素做法總的時間復雜度是O(n×k)O(n\times k)O(n×k)
以上是暴力的做法,也是最容易想到的做法。
單調隊列優化的思路
以求最小值為例:該數組為[1 3 -1 -3 5 3 6 7],k為3。
開始時[1 3 -1] 后移一位變成 [ 3 -1 -3] ,如果 -3 是最小值,那么它前面的 -1 和 3就再也用不到了,是多余的,可以刪掉。 刪掉多余的元素之后, 元素就有了單調性!找滑動窗口內的最小值就是隊頭元素,時間復雜度為O(1) ,那么這道題單調隊列總的時間復雜度就是O(n).
代碼1:數組模擬單調隊列的代碼
思路說完了,上代碼和代碼解釋。
找最小值的思路
一個隊列維護當前窗口中的單調上升的情況。當前窗口的最小值就是隊頭q[tt]。
i表示當前窗口的最右邊下標,k表示窗口長度,則窗口最左邊下標為i-k+1。即當前滑動窗口 為[隊首,隊尾]=[ i-k +1 , i].
隊列數組q中存放的是點的下標,對于數組模擬的隊列q而言,hh表示隊頭,tt表示隊尾,初始化時hh=0,tt=-1,如果hh<=tt表示隊列不空。
何時刪掉隊頭元素 ?因為枚舉的是右端點,受限的就是左端點。當q隊首的值(q中存的是下標)q[hh]小于當前窗口最左邊下標的時候:q[hh]<i-k+1此時隊頭元素刪掉,即hh++。
#include<iostream> using namespace std; const int maxn=1e6+10; int a[maxn],q[maxn]; int main(){int n,k;scanf("%d%d",&n,&k);for(int i=0;i<n;i++) scanf("%d",&a[i]);int hh=0,tt=-1;//隊頭(在左邊)和隊尾(在右邊)for(int i=0;i<n;i++ ){//區間終點是i 那么整個區間是 [ i-k+1,i]//判斷隊頭是否劃出窗口,每次查看一次if(hh<=tt&&q[hh]<i-k+1) hh++;//q存的是下標 while(hh<=tt&&a[q[tt]]>=a[i]) tt--;// 隊列不空,且新來元素更小,把前面所有冗余(比它大的)都清理掉q[++tt]=i;//把當前最小的放進來//當第一次滿足窗口長度k時,往后隨著i的遞增,每次都要輸出最小值,因為窗口在移動if(i>=k-1) printf("%d ",a[q[hh]]);//a【q[]】嚴格單調上升的最小值,是隊頭q[hh]}puts("");//輸出空格return 0;}同理可找最大值,維護一個隊列單調遞減,這樣隊首就是滑動窗口中的最大值。
題目鏈接:acwing154滑動窗口
ac代碼
#include<iostream> using namespace std; const int maxn=1e6+10; int a[maxn],q[maxn]; int main(){int n,k;scanf("%d%d",&n,&k);for(int i=0;i<n;i++) scanf("%d",&a[i]);//找最小值int tt=-1,hh=0; //隊頭hh,隊尾 ttfor(int i=0;i<n;i++){if(hh<=tt&&q[hh]<i-k+1) hh++;while(hh<=tt&&a[q[tt]]>=a[i]) tt--;q[++tt]=i;if(i+1>=k) printf("%d ",a[q[hh]]);}puts("");//找最大值tt=-1,hh=0;for(int i=0;i<n;i++){if(hh<=tt&&q[hh]<i-k+1) hh++;while(hh<=tt&&a[q[tt]]<=a[i]) tt--;//隊尾元素小于當前,全部清空q[++tt]=i;if(i+1>=k) printf("%d ",a[q[hh]]);}puts("");return 0;}代碼2:deque容器實現
acwing中的ac代碼
#include<bits/stdc++.h> using namespace std;const int N=1000010; int a[N]; deque<int> q; int main(){int n,k;cin>>n>>k;for(int i=0;i<n;i++)cin>>a[i];for(int i=0;i<n;i++){if(q.size() && q.front()<i-k+1) q.pop_front();while(q.size() && a[q.back()]>=a[i]) q.pop_back();q.push_back(i);if(i>=k-1) cout<<a[q.front()]<<" ";}cout<<endl;q.clear();for(int i=0;i<n;i++){if(q.size()&& q.front()<i-k+1) q.pop_front();while(q.size()&& a[q.back()]<=a[i]) q.pop_back();q.push_back(i);if(i>=k-1) cout<<a[q.front()]<<" ";}}總結
以上是生活随笔為你收集整理的单调队列板子:求滑动窗口中最大值和最小值的全部內容,希望文章能夠幫你解決所遇到的問題。