栈
栈是只在一段进行插入和删除操作的线性表
特点:先进后出
栈就像一段封闭的羽毛球桶一样,羽毛球只能从一段进入,也只能从这段输出,而且后进入的羽毛球会先被取出,最先进去的羽毛球最后才能被拿出
栈有栈顶和栈底:
栈顶:允许进行插入和删除操作的一段,可以理解为羽毛球桶的开口处
栈底:固定不动的一段,可以理解为羽毛球桶封闭的一段
核心操作:pop(弹出栈顶元素) push(将一元素压入栈中)
例题:洛谷P4387 验证栈序列
思路:怎么样的出栈顺序才叫合法呢??
比如入栈顺序是1,2,3,4,5
出栈的顺序可以为5,4,3,2,1,
也可以为3,2,1,5,4
甚至可以是1,2,3,4,5
第一种情况好理解,就是把数据全部压入栈,再顺序弹出
但第二,三中出栈顺序为什么合法呢?因为我可以先把1,2,3压入栈中,再让它们依次弹出,再压入4和5,再弹出,这就是第二种出栈顺序,故第三种也就好理解了,即压入一个数后立刻将他弹出
那么具体怎么写呢?
模拟方法:
每次入栈一个数,然后用while循环判断,如果栈顶等于现有出队序列的第i个数(i一开始为1),则弹出栈顶,并把i加1,直到把所有元素入栈。
入栈完后,如果栈非空,则flag=0。(flag一开始为1)
最后,如果flag则输出No,否则输出Yes
#include <cstdio>
#include <iostream>
using namespace std;
int top, zhan[100010], n, jin[100010], chu[100010];
void push(int x) { zhan[++top] = x; }
void pop() { top--; }
int main()
{
cin >> n;//读入。
int len, js = 1;
for(int i=0;i<n;i++)
{
cin >> len;//输入访问序列长度。
//读入不再多说。
for (int j = 1; j <= len; j++) cin>>jin[j];
for (int j = 1; j <= len; j++) cin>>chu[j];
for (int j = 1; j <= len; j++)
{
push(jin[j]);//进栈。
while (zhan[top] == chu[js] && zhan[top] && chu[js]) { pop(); js++; }
}//之所以用while循环,是因为可以简单直接的让相同元素出栈。
if (top != 0) puts("No");//如果栈非空,失败。
else puts("Yes");
//初始化,进入下一轮访问。
top = 0;
js = 1;
}
return 0;
}
单调栈
顾名思义:栈内元素单调上升或者下降的栈
尝试:O(1)实现一个栈的插入,删除,查询最大值
思路:我们可以同时开两个栈,一个普通栈sta1,一个单调栈(单调递增)sta2
例如:入栈序列:1,5,4,6
第一次入栈:
sta1:1
sta2:1
第二次入栈:
sta1: 1,5
sta2: 1,5
第三次入栈:
sta1: 1,5,4
sta2: 1,4(发现4比5小,不满足单调栈定义,弹出栈顶元素,将4入栈)
第四次入栈:
sta1: 1,5,4,6
sta2: 1,4,6
这样如果插入和删除用sta1,查询最大值就弹出sta2的栈顶,就达到O(1)复杂度
例题:洛谷P1901
#include<iostream>
using namespace std;
const int maxn=1e6+10;
int s1[maxn],h[maxn],v[maxn],sum[maxn],ans,n,top;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>h[i]>>v[i];
while(top&&h[s1[top]]<h[i])sum[i]+=v[s1[top--]];
sum[s1[top]]+=v[i];
s1[++top]=i;
}
for(int i=1;i<=n;i++)ans=max(ans,sum[i]);
cout<<ans;
return 0;
}
栈的应用:后缀表达式
所谓后缀表达式,就是一种不需要括号的表达式
比如:9+(3-1)*3+10/2的后缀表达式为9 3 1 - 3 * + 10 2 / +
这样的表达看起来是很难受的,但是有机器会喜欢,就是聪明的计算机
计算机怎么方便的算一个表达式呢?
规则:从左到右遍历表达式,遇到数字就进栈,如果遇到符号,就将栈顶两个元素出栈,进行运算,运算结果进栈,一直到最终获得结果
以上面的式子为例,先让9,3,1进栈,遇到减号,将栈中1作为减数出栈,3作为被减数出栈,(这里注意:在处理减号和除号时,一定是栈顶第二个元素减(除)第一个元素)计算结果为2入栈,又读入3,遇到乘号,2和3出栈相乘得6,入栈,以此类推
这里看一道例题:洛谷P1449
代码如下
#include <bits/stdc++.h>
using namespace std;
stack<int> n;
char ch;
int s,x,y;
int main()
{
while(ch!='@')
{
ch=getchar();
switch(ch)
{
case '+':x=n.top();n.pop();y=n.top();n.pop();n.push(x+y);break;
case '-':x=n.top();n.pop();y=n.top();n.pop();n.push(y-x);break;
case '*':x=n.top();n.pop();y=n.top();n.pop();n.push(x*y);break;
case '/':x=n.top();n.pop();y=n.top();n.pop();n.push(y/x);break;
case '.':n.push(s);s=0;break;
default :s=s*10+ch-'0';break;
}
}
printf("%d\n",n.top());
return 0;
}