单调栈

61 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情

栈是一个很好用的STL容器,复杂度是logn的,有pop,pop,empty,push几种常用的操作,栈是遵循一个先进后出的原则,每次进去的元素都要先成为栈顶元素,然后循环下去。 栈也可以用数组模拟,也是很简单的,这里附上acwing的一个栈模拟的题目

828.模拟栈 用数组模拟栈,top代表栈顶,元素入栈就是++top,出栈就是top--,查询输出stk[top],判断栈是否空,就看top是不是等于0。当然直接用STL里面的stack也是可以的,但是复杂度有可能会高一点,其他的都一样。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
#include <cmath>
#include <unordered_map>
#include <stack>
#include <queue>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
typedef pair<int,int> PII;
typedef pair<string,int> PSI;
int gcd(int x,int y){
    return y?gcd(y,x%y):x;
}
ll qmi(ll x,ll y,int mod){
    ll res=1;
    while(y){
        if(y&1) res=res*x%mod;
        y>>=1;
        x=x*x%mod;
    }
    return res;
}
vector<int> alls;
const int N=100010;
int stk[N],top=0,m;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>m;
    while(m--){
        string s;
        int x;
        cin>>s;
        if(s=="push"){
            cin>>x;
            stk[++top]=x;
        }
        if(s=="query"){
            cout<<stk[top]<<"\n";
        }
        if(s=="pop"){
            top--;
        }
        if(s=="empty"){
            if(top==0) cout<<"YES\n";
            else cout<<"NO\n";
        }
    }
    return 0;
}

单调栈

在了解了栈的基础上,我们可以看看的单调栈,单调栈,顾名思义,就是实现一个栈内元素单调递增或者单调递减的栈,可以在nlogn的时间复杂度内求解一些问题,接下来我们可以看一个例子 830.单调栈,这题是要求输出每个数左边第一个比它小的数的值,这题显然我们是可以暴力的,但是肯定得不到满分,只能拿到部分分,因为暴力枚举的话,时间复杂度是n2n^2的,那这题我们可以考虑维护一个单调递增的栈,如果输入的元素比栈顶元素大,那么我们就输出栈顶元素,并且把当前元素入栈,如果输入的元素比栈顶元素小,那么我们就把栈内所有的比当前元素大的值全部弹出,如果栈还是非空的,那么就输出栈顶元素,并且把当前数入栈,否则就输出-1.

数组模拟栈的代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
#include <cmath>
#include <unordered_map>
#include <stack>
#include <queue>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
typedef pair<int,int> PII;
typedef pair<string,int> PSI;
int gcd(int x,int y){
    return y?gcd(y,x%y):x;
}
ll qmi(ll x,ll y,int mod){
    ll res=1;
    while(y){
        if(y&1) res=res*x%mod;
        y>>=1;
        x=x*x%mod;
    }
    return res;
}
const int N=100010;
int stk[N],top=0;
int a[N],n;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        while(x<=stk[top] && top) top--;
        if(top) cout<<stk[top]<<" ";
        else cout<<"-1 ";
        stk[++top]=x;
    }
    return 0;
}

直接用STL的栈的代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
#include <cmath>
#include <unordered_map>
#include <stack>
#include <queue>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
typedef pair<int,int> PII;
typedef pair<string,int> PSI;
stack<int> stk;
int gcd(int x,int y){
    return y?gcd(y,x%y):x;
}
ll qmi(ll x,ll y,int mod){
    ll res=1;
    while(y){
        if(y&1) res=res*x%mod;
        y>>=1;
        x=x*x%mod;
    }
    return res;
}
const int N=100010;
int a[N],n;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        while(!stk.empty()){
            int t=stk.top();
            if(t>=x) stk.pop(); 
            else break;
        }
        if(stk.empty()) cout<<"-1 ";
        else cout<<stk.top()<<" ";
        stk.push(x);
    }
    return 0;
}

接下来看看洛谷一题

【模板】单调栈

题目背景

模板题,无背景。

2019.12.12 更新数据,放宽时限,现在不再卡常了。

题目描述

给出项数为 nn 的整数数列 a1na_{1 \dots n}

定义函数 f(i)f(i) 代表数列中第 ii 个元素之后第一个大于 aia_i 的元素的下标,即 f(i)=mini<jn,aj>ai{j}f(i)=\min_{i<j\leq n, a_j > a_i} \{j\}。若不存在,则 f(i)=0f(i)=0

试求出 f(1n)f(1\dots n)

输入格式

第一行一个正整数 nn

第二行 nn 个正整数 a1na_{1\dots n}

输出格式

一行 nn 个整数 f(1n)f(1\dots n) 的值。

样例 #1

样例输入 #1

5
1 4 2 3 5

样例输出 #1

2 5 4 5 0

提示

【数据规模与约定】

对于 30%30\% 的数据,n100n\leq 100

对于 60%60\% 的数据,n5×103n\leq 5 \times 10^3

对于 100%100\% 的数据,1n3×1061 \le n\leq 3\times 10^61ai1091\leq a_i\leq 10^9

分析

洛谷这个模板题,其实就是要把序列给reverse一下,然后维护一个单调递减的栈即可

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
#include <cmath>
#include <unordered_map>
#include <stack>
#include <queue>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
typedef pair<int,int> PII;
typedef pair<string,int> PSI;
//stack<int> stk;
int gcd(int x,int y){
    return y?gcd(y,x%y):x;
}
ll qmi(ll x,ll y,int mod){
    ll res=1;
    while(y){
        if(y&1) res=res*x%mod;
        y>>=1;
        x=x*x%mod;
    }
    return res;
}
const int N=3000010;

int a[N],n,stk[N],top=0,xb[N];
ll dp[N];
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    reverse(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        while(a[stk[top]]<=a[i] && top) top--;
        if(!top) xb[i]=0;
        else xb[i]=stk[top];
        stk[++top]=i;
    }
    for(int i=n;i>=1;i--){
        if(xb[i]) xb[i]=n+1-xb[i];
        cout<<xb[i]<<" ";
    }
    return 0;
}

希望能帮助到大家,QAQ!