文章目录
- 1、顺序栈 简介
- 2、顺序栈 代码实现
- 3、栈的应用之计算后缀表达式
- 3.1 表达式介绍
- 3.2 计算后缀表达式的实现
- 3.3 完整代码
- 3.4 LeetCode 提交代码
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,栈含有基本操作函数:
P
u
s
h
Push
Push 入栈 ,
P
o
p
Pop
Pop 出栈
算法实现:
- 遇到操作数(0-9)时,一直往后扫描,
P
u
s
h
Push
Push当前的操作数
- 遇到操作符
+
、
−
、
∗
、
/
+、-、*、/
+、−、∗、/ (设为o)时,
b
=
P
o
p
(
)
,
a
=
P
o
p
(
)
b = Pop() , a = Pop ()
b=Pop(),a=Pop() 。计算 a o b 的结果,并
P
u
s
h
Push
Push
- 遍历完后缀表达式后,
P
o
p
(
)
Pop()
Pop() 即为最终运算的结果
注意点:
- 当后缀表达式为一维字符数组时,需注意表达式的遍历,当遇到空格直接跳过,当遇到负号时需特殊判断,负号有可能是运算符也有可能是操作数里的负号。
- 在出栈的时候,首先出栈的是后出现的元素 b,其次出现的才是之前的元素 a, 这个顺序不能颠倒,否则在进行减法和乘法运算时结果就会出错。
- 创建栈时候需注意栈的容量,最大容量根据题目给的范围来定。
3.3 完整代码
在提交代码到 剑指 Offer II 036. 后缀表达式 前,先写一下完整的代码,包括输入输出。
#include <cstdlib>
#include <iostream>
#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;
}
}
bool isDigit(char *s, int i){
return s[i] >= '0' && s[i] <= '9';
}
bool getNum(char *s, int &i, int &x){
if(isDigit(s,i)){
int flag;
if(i >= 1 && s[i-1] == '-')
flag = -1;
else
flag = 1;
int num = s[i++] - '0';
while(isDigit(s, i)){
num = num * 10 + s[i++] - '0';
}
x = num * flag;
return true;
}
else return false;
}
int main(){
printf("开始程序,请输入一个标准的后缀表达式(用空格隔开):\n");
char s[MaxSize * 10];
gets(s);
printf("获取到的后缀表达式为:%s\n", s);
SeqStack S = createStack();
int i = 0;
int x;
int a, b;
while(s[i] != '\0'){
if(s[i] == ' ') { i++ ; continue;}
if(isDigit(s, i)){
getNum(s, i, x);
Push(S, x);
} else {
if(s[i] == '-' && isDigit(s, i+1)) {
i++;
continue;
}
if(Pop(S, b) && Pop(S, a)){
switch (s[i]) {
case '+': Push(S, a + b);break;
case '-': Push(S, a - b); break;
case '*': Push(S, a * b); break;
case '/': Push(S, a / b); break;
default : break;
}
}
}
i ++ ;
}
Pop(S, x);
printf("运算结果: %d\n",x);
return 0;
}
测试数据与结果:
开始程序,请输入一个标准的后缀表达式(用空格隔开):
4 13 5 / +
获取到的后缀表达式为:4 13 5 / +
运算结果: 6
3.4 LeetCode 提交代码
剑指 Offer II 036. 后缀表达式
由于C语言不支持 bool 类型以及 函数中使用&引用参数的地址,这里则提交的是 C++的代码,这里用到了 C++ 的 vector 的遍历,通过使用 for(auto 循环变量:Vector容器变量)
的方式,了解即可。其中辅助函数与之前写的稍有不同,因为这个题目给的输入数据是二维的,相对来说难度就小了一些。
#include <cstdlib>
#include <iostream>
#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;
}
}
bool isDigit(string s, int i){
return s[i] >= '0' && s[i] <= '9';
}
int getNum(string s){
int i = 0;
int flag = 1;
if(s[0] == '-') { flag = -1; i = 1; }
int num = s[i++] - '0';
while(s[i] != '\0' && isDigit(s, i)){
num = num * 10 + (s[i++] - '0');
}
return num * flag;
}
class Solution {
public:
int evalRPN(vector<string>& tokens) {
SeqStack S = createStack();
int x;
int a, b;
for(auto s : tokens){
if(isDigit(s, 0) || isDigit(s, 1)){
Push(S, getNum(s));
} else {
char ch = s[0];
if(Pop(S, b) && Pop(S, a)){
switch (ch) {
case '+': Push(S, a + b); break;
case '-': Push(S, a - b); break;
case '*': Push(S, a * b); break;
case '/': Push(S, a / b); break;
default : break;
}
}
}
}
Pop(S, x);
return x;
}
};
提交结果:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)