1、顺序栈 简介
在上一次的学习中,使用指针实现了链栈 使用 C++ 实现链栈,接下来学一下顺序栈的实现。
回顾一下栈的概念:
栈是只允许在一端(即栈顶)进行插入或删除操作的线性表,属于逻辑结构。
回顾一下栈的数学性质:
n 个不同元素进栈,出栈元素不同排列的个数为 Catalan (卡特兰)数,即 1 n + 1 C 2 n n \frac{1}{n+1}C^{n}_{2n} n+11C2nn
顺序栈是通过顺序表实现的,是用一组 地址连续 的存储单元依次存储数据元素,简单理解就是用数组来存储的。
顺序栈和顺序表共同的特点为:逻辑上相邻的元素在物理位置上也相邻,即逻辑顺序和物理顺序相同。
2、顺序栈 代码实现
接下来是一个最简单的顺序栈的代码实现
#define MaxSize 10004
// 不带头结点的顺序栈
typedef struct {
int data[MaxSize];
int top;
}SeqStack;
// 创建顺序栈
SeqStack createStack(){
SeqStack S;
S.top = -1;
return S;
}
// 判断栈是否为空
bool StackEmpty(SeqStack S){
return S.top == -1;
}
// 入栈
bool Push(SeqStack &S, int x){
if(S.top == MaxSize - 1) {
// 栈满报错
return false;
} else {
S.data[++S.top] = x;
return true;
}
}
// 出栈
bool Pop(SeqStack &S, int &x){
if(StackEmpty(S)) {
return false; // 当为空栈时直接返回
} else {
x = S.data[S.top--];
return true;
}
}
3、栈的应用之计算后缀表达式
接下来我们通过一个题目 剑指 Offer II 036. 后缀表达式 来验证自己实现的顺序栈的正确性。
3.1 表达式介绍
在做提前我们有必要了解一下后缀表达式,相应的还有前缀、中缀表达式。
- 中缀表达式
中缀表达式是一个通用的算术或逻辑公式表示方法,我们平时用的就是中缀表达式,例如,
(1+1) * 2
,结果为4,这种表达式可以轻松用肉眼计算。
特点:除了括号限制运算优先性以外,运算符左右都为运算数
- 前缀表达式
前缀表达式又称波兰表达式,是由一位波兰的数学家提出的,用于简化命题逻辑的一种表达式表示方式。例如
(1+1) * 2
的中缀转为前缀表达式为:* + 1 1 2
特点:不含括号就能表示运算优先性,运算符在两个运算数的前面
- 后缀表达式
后缀表达式又称逆波兰表达式,例如
(1+1) * 2
的中缀转为后缀表达式为:1 1 + 2 *
特点:不含括号就能表示运算优先性,运算符在两个运算数的后面
在考研考题中经常会考察使用栈来实现表达式的转换,这里主要了解后缀表达式。
记录一下中缀转为前缀和后缀的要点:
- 中缀转后缀:
左优先原则:只要左边运算符能先计算,就优先计算左边的。
例: 1+(1*2*3)
,转为后缀为, 1 1 2 * 3 * +
,这里注意到先计算的是 (1*2*3)
,不过是先计算1 * 2
,即 1 2 *
,然后在将这个结果视为一个数继续与 * 3
进行计算。
- 中缀转后缀:
右优先原则:只要右边的运算符能先计算就先计算右边的。
例: 1+(1*2*3)
,转为前缀为, + 1 * 1 * 2 3
3.2 计算后缀表达式的实现
后缀表达式特点:运算符在操作数的后面,于是当我们遇到运算符时,直接往前面找操作数进行运算即可。只需要用一个栈来存储操作数。
设存储操作数的栈为 S S S,栈含有基本操作函数: