分为单调递增和单调递减栈。(栈内元素成递增或者递减性)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'); }