「数据结构详解·三」栈

1. 栈的定义、构成、术语和性质

栈(Stack),是一种重要的线性表

栈类似于几只碗摞在一起:
栈
现在这个栈的最上面的“碗”,也就是下图中箭头所指的,被称作栈顶
在这里插入图片描述

我们现在要再放入一只“碗”,如下图:
入栈
这个操作被称为入栈
我们如果要拿掉一只“碗”,就像这样:
出栈
这个操作被称为出栈
我们一摞碗要想拿中间的碗,不可以直接拿。栈也一样,我们只能访问栈顶元素,其他元素不可直接访问

2. 栈的实现

主要有以下两种。

2-1. 数组模拟

定义:

int stk[114514],tp,len;

stk[] 表示整个栈,tp 表示栈顶元素,len 表示栈的长度。
访问栈长度:

int size()
{
	return len;
}

判断栈是否为空:

bool empty()
{
	return !len;
}

入栈:

void push(int x)
{
	stk[++len]=tp=x;
}

出栈:

void pop()
{
	if(empty()) puts("ERROR");//特判
	else tp=stk[--len];
}

访问栈顶元素:

int top()
{
	return tp;
}

2-2. C++ STL stack

我们也通常使用 C++ STL 中的 stack 来实现:

stack<int>stk;//定义元素为整型的 stack

STL stack 中为我们提供如下函数:

  • push(x):将元素 x x x 入栈。
  • pop():弹出栈顶。
  • empty():判断栈是否为空。
  • size():返回栈长度。
  • top():返回栈顶。

3. 例题详解

3-1. 洛谷 B3614 【模板】栈

栈模板。
示例代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);//加快输入输出
	int t;
	cin>>t;
	while(t--)
	{
		stack<unsigned long long>a;//要开 ull
		int n;
		cin>>n;
		while(n--)
		{
			string s;
			cin>>s;
			if(s=="push")
			{
				unsigned long long x;
				cin>>x;
				a.push(x);
			}
			else if(s=="pop")
			{
				if(a.empty()) cout<<"Empty"<<endl;
				else a.pop();
			}
			else if(s=="query")
			{
				if(a.empty()) cout<<"Anguei!"<<endl;
				else cout<<a.top()<<endl;
			}
			else cout<<a.size()<<endl;
		}
	}
	return 0;
}

3-2. 洛谷 P1739 表达式括号匹配

这是栈的重要运用之一。
以样例二 (25+x)*(a*(a+b+b)@ 为例。
忽略非括号的,首先读入的是 (,压入栈。

┌———┬——————————
| ( |
└———┴——————————

然后读入到 ),发现和栈顶匹配,弹出栈顶。

┌——————————————
|
└——————————————

接下来读到 (,入栈。

┌———┬——————————
| ( |
└———┴——————————

再接着是 (,入栈。

┌———┬———┬——————
| ( | ( |
└———┴———┴——————

最后读入到 ),和栈顶匹配,出栈。

┌———┬——————————
| ( |
└———┴——————————

我们发现,最后栈内还有东西——我们已经类似贪心地将括号全部匹配完,故括号表达式不匹配。
参考代码:

#include<bits/stdc++.h>
using namespace std;

stack<char>a;

int main()
{
	char c;
	while((c=getchar())!='@')
	{
		if(c!='('&&c!=')') continue;
		if(c=='(') a.push(c);
		else if(a.empty())
		{
			puts("NO");
			return 0;
		}
		else if(a.top()=='(') a.pop();
	}
	puts(a.empty()?"YES":"NO");
 	return 0;
}

3-3. 洛谷 P1449 后缀表达式

计算表达式是另一个重要的运用。
以样例 3.5.2.-*7.+@ 为例。
首先读入数字 3 3 3,压入栈。

┌———┬——————————
| 3 |
└———┴——————————

接着读入数字 5 5 5,入栈。

┌———┬———┬——————
| 3 | 5 |
└———┴———┴——————

再读入数字 2 2 2,入栈。

┌———┬———┬———┬——
| 3 | 5 | 2 |
└———┴———┴———┴——

读入符号 - \texttt- -,弹出栈的两个元素 2 , 5 2,5 2,5,计算 5 − 2 = 3 5-2=3 52=3,把 3 3 3 压入栈。

┌———┬———┬——————
| 3 | 3 |
└———┴———┴——————

读入符号 * \texttt* *,弹出栈的两个元素 3 , 3 3,3 3,3,计算 3 × 3 = 9 3\times3=9 3×3=9,把 9 9 9 压入栈。

┌———┬——————————
| 9 |
└———┴——————————

读入数字 7 7 7,压入栈。

┌———┬———┬——————
| 9 | 7 |
└———┴———┴——————

最后读入符号 + \texttt+ +,弹出栈的两个元素 7 , 9 7,9 7,9,计算 9 + 7 = 16 9+7=16 9+7=16,把 16 16 16 压入栈。

┌————┬—————————
| 16 |
└————┴—————————

最后栈中的 16 16 16 即为所求。
这便是栈在后缀表达式中的运用。
示例代码:

#include<bits/stdc++.h>
#define f(x,y,c) c=='+'?y+x:c=='-'?y-x:c=='*'?y*x:y/x//注意 x,y 在原栈中的顺序,可能要颠倒再计算
using namespace std;

stack<int>a;

int main()
{
	char c;
	while((c=getchar())!='@')
	{
		if(isdigit(c))
		{
			int s=c-48;
			while(isdigit(c=getchar()))
			{
				s=s*10+c-48;
			}//读入数字
			a.push(s);
		}
		else
		{
			int x=a.top();
			a.pop();
			int y=a.top();
			a.pop();
			a.push(f(x,y,c));
		}
	}
	cout<<a.top();
 	return 0;
}

4. 巩固练习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值