栈&单调栈
栈
后进先出
数组模拟即可
例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;
}