单调栈和单调队列入门

单调栈是什么?

分为单调递增和单调递减栈。(栈内元素成递增或者递减性)

例如:
当前单调递增栈内元素[1,2,4],之后入栈的元素为(3),
为了维护单调性,元素(4)出栈

\[ [1,2,4]-入栈(3) -出栈(4)- [1,2,3]-入栈(0)-出栈(1)(2)(3)-[0] \]

单调递增栈主要作用:

把序列中每个元素放到单调栈中进行维护就可以在\(O(n)\)时间复杂度内
求出区间每个元素为最大值/最小值时,影响的区间范围为[left,right]。

单调递增↗栈求最小值影响范围

单调递减↘栈求最大值影响范围

\(例如:序列{1,2,3,2,1}\)

    1 2 3 2 1
        口
      口口口
    口口口口口
    0 1 2 3 4

用单调递减栈即可求出

最大值区间[left,right]
1[0,0]
2[0,1]
3[0,4]
2[3,4]
1[4,4]

维护单调栈:

这里我们以单调递增栈为例,求最小值影响范围

我们规定将下标(index)压入栈中。为了方便编码,我们在使用单调栈的数组的最后加入-INF(一个极小值),方便后续的出栈。

序列变成 \({1,2,3,2,1,-INF}\)

i要入栈的height[i]栈的变动变动后的栈
01push(0)[0]
12push(1)[0,1]
23push(2)[0,1,2]
32pop(2),push(3)[0,1,3]
41pop(3),pop(1),push(4)[0,4]
5-INFpop(0),push(4)[]

[left,right]中的right:

若元素height[i]从栈中pop出就说明这个元素为最小值的右侧影响范围到此为止。

[left,right]中的left:

因为栈内元素单调递增,栈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');
}

题目

P1886 滑动窗口

P1440 求m区间内最小值

转载于:https://www.cnblogs.com/--zz/p/11247874.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值