分爲單調遞增和單調遞減棧。(棧內元素成遞增或者遞減性)c++
例如:
當前單調遞增棧內元素[1,2,4],以後入棧的元素爲(3),
爲了維護單調性,元素(4)出棧數組
\[ [1,2,4]-入棧(3) -出棧(4)- [1,2,3]-入棧(0)-出棧(1)(2)(3)-[0] \]編碼
把序列中每一個元素放到單調棧中進行維護就能夠在\(O(n)\)時間複雜度內
求出區間每一個元素爲最大值/最小值時,影響的區間範圍爲[left,right]。spa
單調遞增↗棧求最小值影響範圍code
單調遞減↘棧求最大值影響範圍隊列
\(例如:序列{1,2,3,2,1}\)ci
1 2 3 2 1 口 口口口 口口口口口 0 1 2 3 4
用單調遞減棧便可求出get
最大值 | 區間[left,right] |
---|---|
1 | [0,0] |
2 | [0,1] |
3 | [0,4] |
2 | [3,4] |
1 | [4,4] |
咱們規定將下標(index)壓入棧中。爲了方便編碼,咱們在使用單調棧的數組的最後加入-INF(一個極小值),方便後續的出棧。it
序列變成 \({1,2,3,2,1,-INF}\)table
i | 要入棧的height[i] | 棧的變更 | 變更後的棧 |
---|---|---|---|
0 | 1 | push(0) | [0] |
1 | 2 | push(1) | [0,1] |
2 | 3 | push(2) | [0,1,2] |
3 | 2 | pop(2),push(3) | [0,1,3] |
4 | 1 | pop(3),pop(1),push(4) | [0,4] |
5 | -INF | pop(0),push(4) | [] |
若元素height[i]從棧中pop出就說明這個元素爲最小值的右側影響範圍到此爲止。
由於棧內元素單調遞增,棧pop以後,棧頂的元素height[s.top()]不大於pop的元素。因此左側的影響範圍爲pop以後棧頂的元素的下標+1,這裏須要注意pop以後棧爲空的狀況,由於pop以後棧爲空,說明沒有元素是比pop出的這個元素小,那這個pop出的元素影響它左端的全部元素。
//單調遞增棧 #include<bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; typedef long long int LL; const int MAXN = 1e6 + 1; LL height[MAXN]; int N; void solve(){ height[N] = -INF; stack<int> s; for(int i=0;i<=N;i++){ while(!s.empty() && height[s.top()] > height[i]){ int cur = s.top(); s.pop(); int _left = s.empty()?0:s.top()+1; int _right = i-1; cout << height[cur] << " " << _left << " " << _right << endl; } s.push(i); } } int main() { cin >> N; for(int i=0;i<N;i++) cin >> height[i]; solve(); return 0; }
//單調遞減棧 #include<bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; typedef long long int LL; const int MAXN = 1e6 + 1; LL height[MAXN]; int N; void solve(){ height[N] = INF; stack<int> s; for(int i=0;i<=N;i++){ while(!s.empty() && height[s.top()] < height[i]){ int cur = s.top(); s.pop(); int _left = s.empty()?0:s.top()+1; int _right = i-1; cout << height[cur] << " " << _left << " " << _right << endl; } s.push(i); } } int main() { cin >> N; for(int i=0;i<N;i++) cin >> height[i]; solve(); return 0; }
void solve(){ //單調遞增棧 -INF,遞減 INF height[N] = -INF; stack<int> s; for(int i=0;i<=N;i++){ //單調遞增棧 >,遞減 <,等號看題目 while(!s.empty() && height[s.top()] > height[i]){ int cur = s.top(); s.pop(); int _left = s.empty()?0:s.top()+1; int _right = i-1; cout << height[cur] << " " << _left << " " << _right << endl; } s.push(i); } }
引入雙端隊列的概念。
元素能夠從隊列的頭部和尾部進行插入和刪除。
那麼單調隊列和單調棧的區別在於棧與雙端隊列的區別,在原有單調棧的基礎上,你能夠修改和獲取到棧底的元素,這就致使了你能夠對最值影響區間[Left,Right]中的Left進行控制,而且能夠直接得到這個區間最值是多少。(本來由於棧頂元素未知,因此沒法獲取),也就是說能夠 在\(O(n)\)求整個序列中,區間長度爲k的區間最值
//輸出區間[left,right],長度爲m的最小值. inline void solve(){ deque<int> q; for(int i=0;i<n;i++) { //單調遞增棧 >,遞減 < while(!q.empty()&&height[q.back()]>height[i]) q.pop_back(); //如下根據題意進行更改 printf(i>0?"0\n":"%d\n",height[q.front()]); q.push_back(i); if(i-q.front()>=m) q.pop_front();//限制區間長度爲m } putchar('\n'); }