栈&单调栈

后进先出

数组模拟即可

例1

设计一个支持push,pop,top等操作并且可以在O(1)时间内检索出最小元素的堆栈

显然一个栈维护最小值时,pop后无法O(1)知道其他最小值

二叉堆是支持查最值的,但是O(logn)的

所以我们可以建两个栈,一个栈存原数据,另一个维护从栈顶到now的min

每次一起push ,pop 即可

class MinStack {
public:
    stack<int> ans,ans_min;
    MinStack() {

    }
    void push(int x) {
        ans.push(x);
        if (ans_min.empty() || ans_min.top()>x)
            ans_min.push(x);
        else
            ans_min.push(ans_min.top());
    }
    void pop() {
        ans.pop();
        ans_min.pop();
    }
    int top() {
        return ans.top();
    }
    int getMin() {
        return ans_min.top();
    }
};

例2:Editor

中文https://www.acwing.com/problem/content/description/130/

D,L,R 的时候要特判栈不空

#include <iostream>
#include <cstdio>
using namespace std;
const int N=1e6+5;
int m,x,top1,top2,L[N],R[N],f[N],sum;
char a;
inline int Max(int x,int y){return x>y?x:y;}
int main(){
    while(scanf("%d",&m)!=EOF){
        top1=top2=sum=0;
		f[0]=-0x7fffffff;
        while(m--){
            cin>>a;
            if(a=='I'){
                scanf("%d",&x);
                L[++top1]=x;
                f[top1]=Max(f[top1-1],sum+=x);
            }
            if(a=='R'&&top2){
                x=L[++top1]=R[top2--];
                f[top1]=Max(f[top1-1],sum+=x);
            }
            if(a=='Q')scanf("%d",&x),printf("%d\n",f[x]);
            if(a=='L'&&top1){
				sum-=L[top1];
				R[++top2]=L[top1--];
			}
            if(a=='D'&&top1) sum-=L[top1--];
        }
    }
}

前缀中缀后缀表达式

  
// 递归法求中缀表达式的值,O(n^2)
int calc(int l, int r) {
	// 寻找未被任何括号包含的最后一个加减号
	for (int i = r, j = 0; i >= l; i--) {
		if (s[i] == '(') j++;
		if (s[i] == ')') j--;
		if (j == 0 && s[i] == '+') return calc(l, i - 1) + calc(i + 1, r);
		if (j == 0 && s[i] == '-') return calc(l, i - 1) - calc(i + 1, r);
	}
	// 寻找未被任何括号包含的最后一个乘除号
	for (int i = r, j = 0; i >= l; i--) {
		if (s[i] == '(') j++;
		if (s[i] == ')') j--;
		if (j == 0 && s[i] == '*') return calc(l, i - 1) * calc(i + 1, r);
		if (j == 0 && s[i] == '/') return calc(l, i - 1) / calc(i + 1, r);
	}
	// 首尾是括号
	if (s[l] == '('&&s[r] == ')') return calc(l + 1, r - 1);
	// 是一个数
	int ans = 0;
	for (int i = l; i <= r; i++) ans = ans * 10 + s[i] - '0';
	return ans;
}

// ----------------------------------------------------
// 后缀表达式转中缀表达式,同时求值,O(n)

// 数值栈 
vector<int> nums; 
// 运算符栈 
vector<char> ops;

// 优先级 
int grade(char op) {
	switch (op) {
	case '(':
		return 1;
	case '+':
	case '-':
		return 2;
	case '*':
	case '/':
		return 3;
	}
	return 0;
}

// 处理后缀表达式中的一个运算符 
void calc(char op) {
	// 从栈顶取出两个数 
	int y = *nums.rbegin();
	nums.pop_back();
	int x = *nums.rbegin();
	nums.pop_back();
	int z;
	switch (op) {
	case '+':
		z = x + y;
		break;
	case '-':
		z = x - y;
		break;
	case '*':
		z = x * y;
		break;
	case '/':
		z = x / y;
		break;
	}
	// 把运算结果放回栈中 
	nums.push_back(z);	
}

// 中缀表达式转后缀表达式,同时对后缀表达式求值 
int solve(string s) {
	nums.clear();
	ops.clear();
	int top = 0, val = 0;
	for (int i = 0; i < s.size(); i++) {
		// 中缀表达式的一个数字 
		if (s[i] >= '0' && s[i] <= '9') {
			val = val * 10 + s[i] - '0';
			if (s[i+1] >= '0' && s[i+1] <= '9') continue;
			// 后缀表达式的一个数,直接入栈 
			nums.push_back(val);
			val = 0;
		}
		// 中缀表达式的左括号 
		else if (s[i] == '(') ops.push_back(s[i]);
		// 中缀表达式的右括号 
		else if (s[i] == ')') {
			while (*ops.rbegin() != '(') {
				// 处理后缀表达式的一个运算符 
				calc(*ops.rbegin());
				ops.pop_back();
			}
			ops.pop_back();
		}
		// 中缀表达式的加减乘除号 
		else {
			while (ops.size() && grade(*ops.rbegin()) >= grade(s[i])) {
				calc(*ops.rbegin());
				ops.pop_back();
			}
			ops.push_back(s[i]);
		} 
	}
	while (ops.size()) {
		calc(*ops.rbegin());
		ops.pop_back();
	}
	// 后缀表达式栈中最后剩下的数就是答案 
	return *nums.begin();
}

单调栈

利用单调栈,可以找到从左/右遍历第一个比它小/大的元素的位置

模板

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=3000006;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n;
int f[N],ans[N],s[N],top;
int main() {
	n=read();
	for(int i=1;i<=n;i++) f[i]=read();
	for(int i=n;i;i--) {
		while(top&&f[s[top]]<=f[i]) top--;
		ans[i]=s[top];
		s[++top]=i;
	}
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}

最大矩形面积

栈里保存矩形,矩形高度递增,

从左到右扫描,遇到比st[top]高的矩阵就入栈,令w[top]=1

否则不断弹出比st[top]小的,然后累加width更新ans,直到遇到比st[top]高的矩阵或栈空就入栈,然后令w[top]=width+1(继承前面的宽度)

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N=100005;
long long max(long long x,long long y){return x<y?y:x;}
int n,a[N];
int s[N],top,w[N];
long long ans=0;
int main() {
    while(1){
        ans=0;
        memset(w,0,sizeof(w));
        scanf("%d",&n);
        if(!n) return 0;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        a[n+1]=0;
        for(int i=1;i<=n+1;i++) {
            if(a[i]>s[top])
                s[++top]=a[i],w[top]=1;
            else {
                int width=0;
                while(a[i]<s[top]) {
                    width+=w[top];
                    ans=max(ans,(long long)width*s[top]);
                    top--;
                }
                s[++top]=a[i];w[top]=width+1;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2020-08-20 16:54  ke_xin  阅读(25)  评论(0编辑  收藏  举报