数据结构基础——栈

栈是只在一段进行插入和删除操作的线性表
特点:先进后出
栈就像一段封闭的羽毛球桶一样,羽毛球只能从一段进入,也只能从这段输出,而且后进入的羽毛球会先被取出,最先进去的羽毛球最后才能被拿出
栈有栈顶和栈底
栈顶:允许进行插入和删除操作的一段,可以理解为羽毛球桶的开口处
栈底:固定不动的一段,可以理解为羽毛球桶封闭的一段
核心操作: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青云遮夜雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值