本篇博客部分内容出自《2022数据结构考研复习指导》,仅作个人学习记录。 |
目录
- 一、中序表达式转后序表达式的目的
- 二、转换步骤
- 三、isp和icp的含义
- 四、具体例子
- 五、转后序表达式实现代码
- 六、后序表达式的计算方法
- 七、计算后序表达式实现代码
一、中序表达式转后序表达式的目的
表达式求值是程序设计设计语言编译中一个最基本的问题。中序表达式不仅要依赖运算符的优先级,还要处理括号。如果计算机直接计算中序表达式,会用到大量if-else语句判断数字,运算符以及括号的关系,非常复杂。
后序表达式的运算符在操作数后面,在后序表达式中已经考虑了运算符的优先级,没有括号,只有操作数和运算符,无论多复杂的表达式都只需要一个栈进行运算。因此,后序表达式更适合被计算机运行。
二、转换步骤
从左往右开始扫描中序表达式;
遇到数字时,加入后序表达式;
遇到运算符时:
- a.若为’(’,入栈;
- b.若为’)’,则依次把栈中的运算符加入后序表达式,直到栈中出现’(’,从栈中删除’)’;
- c.若为除括号外的其他运算符,当其优先级高于除‘(’外的栈顶运算符时,直接入栈。否则从栈顶开始,依次弹出比当前处理的运算符优先级高和优先级相等的运算符,直到一个比它优先级低的或遇到一个左括号为止。
当扫描的中序运算符结束时,栈中的所有运算符依次出栈加入后序表达式。
三、isp和icp的含义
从上面可知,转换过程最难也是最重要的部分就是运算符的优先级的比较。接下来就讲解这部分。
运算符的优先级如下表所示,其中isp(in stack priority)是栈内优先,icp(in coming priority)是栈外优先。
操作符 | # | ( | * , / | + , - | } |
isp | 0 | 1 | 5 | 3 | 6 |
icp | 0 | 6 | 4 | 2 | 1 |
这个表是怎么的这里就不展开讨论了,感兴趣的读者可以看看这个博客:
icp和isp的含义是什么呢?
由转换步骤的a可知,当遍历到中序表达式的一个运算符时,如果不是’(‘和’)’,需要比较中序表达式运算符的优先级的栈顶运算符的优先级,具体是:取中序表达式运算符的栈外优先级icp(因为中序表达式运算符是在栈外)与栈顶运算符的栈内优先级isp(因为栈顶运算符是在栈内)进行比较。
各个运算符的优先级如何理解?
- ‘#’:这个符号代表中序表达式的开始与结束。当遍历到开头的’#’,直接放入栈内,因为’#‘的栈内优先级是最小的,所以对后面的运算符比较不会造成任何影响。当遍历到结尾的’#’,因为它的栈外优先级是最小的,所以会让栈里的运算符依次出栈输出然后结束。
- ‘(’和’)’:在中序表达式中,我们要优先计算括号里的内容,括号里面是优先级最高的而在后续表达式中,要如何做到这个特性呢?首先对于’(’,当’(‘为栈外运算符时,它的优先级最高,所以可以直接入栈。入栈后优先级变成除了’#‘之外最低;对于’)’,当’)‘为栈外运算符时,它的优先级是除了’#'最低的,这是为了把括号里面运算符都出栈并输出,以保证括号里的内容先进行运算。
- ‘+ , -’:在中序表达式中,我们要先计算’*,/‘再计算’+,-’,而这就要使’+,-‘的栈内和栈外的优先级都比’*,/‘的栈内优先级低,这是因为一旦遍历到’+,-’,就可以让栈里的’*,/‘出栈并输出,再让’+,-‘入栈,以达到先计算’*,/‘再计算’+,-‘的目的。而’+,-‘的栈内优先级比栈外优先级高是因为如果连续遇到两个’+,-‘的运算符时,我们应该从左到右运算,即先算栈内的,再算栈外的,’+,-'的栈内优先级比栈外优先级高
- ‘* , /’:和’+,-'的原理相似。
四、具体例子
将中序表达式#a+b-a*((c+d)/e-f)+g#转换成后序表达式
- #,#直接进栈。
栈内内容:’#’
输出:’’ - a,a为操作数,直接输出。
栈内内容:’#’
输出:‘a’ - +,因为isp(#)<icp(+),将+入栈。
栈内内容:’#+’
输出:‘a’ - b,b为操作数,直接输出。
栈内内容:’#+’
输出:‘ab’ - -,因为isp(+)>icp(-),所以+出栈并输出;因为isp(#)<icp(-),所以-入栈。
栈内内容:’#-’
输出:‘ab+’ - a,a为操作数,直接输出。
栈内内容:’#-’
输出:‘ab+a’ - *,因为isp(-)<icp(*),所以将*入栈。
栈内内容:’#-*’
输出:‘ab+a’ - (,因为isp(*)<icp()),所以(入栈。
栈内内容:’#-*(’
输出:‘ab+a’ - (,因为isp(()<icp()),所以(入栈。
栈内内容:’#-*((’
输出:‘ab+a’ - c,c是操作数,直接输出。
栈内内容:’#-*((’
输出:‘ab+ac’ - +, 因为isp(()<icp(+),所以+进栈。
栈内内容:’#-*((+’
输出:‘ab+ac’ - .d,d是操作数,直接输出。
栈内内容:’#-*((+’
输出:‘ab+acd’ - ),isp(+)>icp()),所以+出栈并输出;因为isp(()==icp()),所以(出栈。
栈内内容:’#-*(’
输出:‘ab+acd+’ - /,因为isp(()<icp(/),所以/进栈。
栈内内容:’#-*(/’
输出:‘ab+acd+’ - e,e为操作数,直接输出。
栈内内容:’#-*(/’
输出:‘ab+acd+e’ - -,因为isp(/)>icp(-),所以/出栈并输出;因为isp(()<icp(-),所以-入栈。
栈内内容:’#-*(-’
输出:‘ab+acd+e/’ - f,f为操作数,直接输出。
栈内内容:’#-*(-’
输出:‘ab+acd+e/f’ - ),isp(-)>icp()),所以-出栈并输出;因为isp(()==icp()),所以(出栈。
栈内内容:’#-*’
输出:‘ab+acd+e/-’ - +,isp(*)>icp(+),所以*出栈并输出;因为isp(-)==icp(+),所以-出栈并输出;因为isp(#)<icp(+),所以+入栈。
栈内内容:’#+’
输出:‘ab+acd+e/f-*-’ - g,g为操作数,直接输出。
栈内内容:’#+’
输出:‘ab+acd+e/f-*-g’ - #,因为isp(+)>icp(#),所以+出栈并输出;因为isp(#)==icp(#),所以#出栈并结束。
栈内内容:’’
输出:‘ab+acd+e/f-*-g+’
五、转后序表达式实现代码
#include <iostream>
#include<map>
#include <cstring>
using namespace std;
#define MaxSize 50
typedef struct {
char data[MaxSize];
int top;
}SqStack;
//初始栈
void InitStack(SqStack &S) {
S.top = -1;
}
//入栈
bool Push(SqStack &S, char x) {
if (S.top == MaxSize - 1)
return false;
S.data[++S.top] = x;
return true;
}
//出栈
bool Pop(SqStack& S, char &x) {
if (S.top == -1)
return false;
x = S.data[S.top--];
return true;
}
//查看栈顶元素
bool GetPop(SqStack S, char& x) {
if (S.top == -1)
return false;
x = S.data[S.top];
return true;
}
int main()
{
map<char, int> isp;
map<char, int> icp;
isp = {{ '#', 0},{ '(', 1},{ '*', 5},{ '/', 5},{ '+', 3},{ '-', 3},{ '-', 6}};
icp = {{ '#', 0},{ '(', 6},{ '*', 4},{ '/', 4},{ '+', 2},{ '-', 2},{ '-', 1}};
string expression1 = "#a+b-a*((c+d)/e-f)+g#"; //中序表达式
string expression2 = ""; //后序表达式
SqStack S;
InitStack(S);
for (int i = 0; i < expression1.length(); i++) {
//如果是数字,直接加入后序表达式
if (expression1[i] >= 'a' && expression1[i] <= 'z'){
expression2 = expression2 + expression1[i];
}
//如果是'('直接加入后序表达式
if (expression1[i] == '(')
Push(S, expression1[i]);
//如果是')'
if(expression1[i] == ')')
for (int j=S.top; j>=0 ; j--){
char temp;
GetPop(S, temp);
if (temp != '('){
Pop(S, temp);
expression2 = expression2 + temp;
}
else{
Pop(S, temp);
break;
}
}
//如果是'#'
if (expression1[i] == '#')
if(i==0)
Push(S, expression1[i]);
else
for (int j = S.top; j >= 0; j--){
char temp;
GetPop(S, temp);
if (temp != '#'){
Pop(S, temp);
expression2 = expression2 + temp;
}
else{
Pop(S, temp);
break;
}
}
if(expression1[i]=='*' || expression1[i]=='/' || expression1[i] == '+' || expression1[i] == '-')
for (int j = S.top; j >= 0; j--){
char temp;
GetPop(S, temp);
if (isp[temp]<icp[expression1[i]]){
Push(S, expression1[i]);
break;
}
else{
Pop(S, temp);
expression2 = expression2 + temp;
}
}
}
cout << "中序表达式:" << expression1 << endl;
cout << "后序表达式:" << expression2 << endl;
}
六、后序表达式的计算方法
扫描后序表达式的每一项,根据它的类型做如下相应操作:
- 若该项为操作数,则将其压入栈中。
- 若该项是操作符<op>,则连续从栈中推出两个操作数Y和X,形成运算指令X<op>Y,并将计算结果重新压入栈中。
当后序表达式的所有项都扫描并处理完成后,栈顶存放的就是最后的计算结果。
七、计算后序表达式实现代码
#include <iostream>
using namespace std;
#define MaxSize 50
typedef struct {
char data[MaxSize];
int top;
}SqStack;
//初始栈
void InitStack(SqStack& S) {
S.top = -1;
}
//入栈
bool Push(SqStack& S, char x) {
if (S.top == MaxSize - 1)
return false;
S.data[++S.top] = x;
return true;
}
//出栈
bool Pop(SqStack& S, char& x) {
if (S.top == -1)
return false;
x = S.data[S.top--];
return true;
}
//查看栈顶元素
bool GetPop(SqStack S, char& x) {
if (S.top == -1)
return false;
x = S.data[S.top];
return true;
}
int main()
{
SqStack S;
InitStack(S);
string expression = "326+*"; //后序表达式 中序表达式的形式为3*(2+6)
for (int i = 0; i < expression.length(); i++){
if (expression[i] >= '0' && expression[i] <= '9')
Push(S, expression[i]);
else {
int Y, X;
char temp;
Pop(S, temp);
Y = int(temp) - 48; //将char类型数字传换成int类型数字
Pop(S, temp);
X = int(temp) - 48;
cout << X << " " << Y<<endl;
if (expression[i] == '+')
Push(S, char(X + Y + 48)); 将int类型数字传换成char类型数字再入栈
else if(expression[i] == '-')
Push(S, char(X - Y + 48));
else if (expression[i] == '*')
Push(S, char(X * Y + 48));
else if (expression[i] == '/')
Push(S, char(X / Y + 48));
}
}
int result;
char temp;
Pop(S, temp);
result = int(temp) - 48;
cout << "后续表达式:" << expression << endl;
cout << "结果为:" << result << endl
}