本篇博客源于自己在做王道考研数据结构复习书的习题,只介绍解法,不说原理哦~(原理我也不知道)
先看一道简单的的题目:将中缀表达式 a / b + ( c ∗ d − e ∗ f ) / g a/b+(c*d-e*f)/g a/b+(c∗d−e∗f)/g转换成后缀表达式,当然用手来推,还是相对简单的,那怎么设计程序来自动实现呢?
思想如下:
- 从左到右开始扫描中缀表达式:
- 遇到数字时,加入后缀表达式:
- 遇到运算符时:
- 若为
'('
,则入栈; - 若为
')'
,则依次把栈中的运算符加入后缀表达式,直到出现'('
,从栈中删除'('
; - 若为除括号外的其他运算符,当其优先级高于除
'('
外的栈顶运算符时,直接入栈。否则从栈顶开始,依次弹出比当前处理的运算符优先级高和优先级相等的运算符,直到一个比它优先级低的或遇到了一个左括号为止。
- 若为
如果只看上面说的,我相信你可能不太懂,所以下面我们拿一道例题来一步一步看看。
待处理序列 | 栈 | 后缀表达式 | 当前扫描元素 | 动作 |
---|---|---|---|---|
a / b + ( c ∗ d − e ∗ f ) / g a/b+(c*d-e*f)/g a/b+(c∗d−e∗f)/g | a | a加入到后缀表达式 | ||
/ b + ( c ∗ d − e ∗ f ) / g /b+(c*d-e*f)/g /b+(c∗d−e∗f)/g | a | / | /入栈 | |
b + ( c ∗ d − e ∗ f ) / g b+(c*d-e*f)/g b+(c∗d−e∗f)/g | / | a | b | b加入到后缀表达式 |
+ ( c ∗ d − e ∗ f ) / g +(c*d-e*f)/g +(c∗d−e∗f)/g | / | ab | + | +优先级低于栈顶的/,弹出/ |
+ ( c ∗ d − e ∗ f ) / g +(c*d-e*f)/g +(c∗d−e∗f)/g | ab/ | + | +入栈 | |
( c ∗ d − e ∗ f ) / g (c*d-e*f)/g (c∗d−e∗f)/g | + | ab/ | ( | ( 入栈 |
c ∗ d − e ∗ f ) / g c*d-e*f)/g c∗d−e∗f)/g | +( | ab/ | c | c加入到后缀表达式 |
∗ d − e ∗ f ) / g *d-e*f)/g ∗d−e∗f)/g | +( | ab/c | * | 栈顶为( ,*入栈 |
d − e ∗ f ) / g d-e*f)/g d−e∗f)/g | +(* | ab/c | d | d加入到后缀表达式 |
− e ∗ f ) / g -e*f)/g −e∗f)/g | +(* | ab/cd | - | - 优先级低于栈顶的*,弹出* |
− e ∗ f ) / g -e*f)/g −e∗f)/g | +( | ab/cd* | - | 栈顶为( ,- 入栈 |
e ∗ f ) / g e*f)/g e∗f)/g | +(- | ab/cd* | e | e加入到后缀表达式 |
∗ f ) / g *f)/g ∗f)/g | +(- | ab/cd*e | * | *优先级高于栈顶的- ,*入栈 |
f ) / g f)/g f)/g | +(-* | ab/cd*e | f | f加入到后缀表达式 |
)/g | +(-* | ab/cd*ef | ) | 把栈中( 前面的的符号依次加入表达式 |
/g | + | ab/cd*ef*- | / | /的优先级高于栈顶的+,/入栈 |
g | +/ | ab/cd*ef*- | g | g加入到后缀表达式 |
+/ | ab/cd*ef*-g | 扫描完毕,把剩下的运算符依次加入表达式 | ||
ab/cd*ef*-g/+ | 转换完成 |
最后的结果 a b / c d ∗ e f ∗ − g / + ab/cd*ef*-g/+ ab/cd∗ef∗−g/+ 就是要找的后缀表达式,可以自己那手工转换的来对比验证一下,结果肯定是一样的。
上面这一题碰巧没有遇到优先级相同的情况,下面我们来示范一个遇到优先级相同的情况下,该怎么做。
为了运算方便,我们定义了如下的优先级(具体数值没有关系,只是想表达优先级高低),我们把还没进栈的优先级表示为icp
(栈外优先,in coming priority),把进栈后的运算符优先级表示为isp
(栈内优先,in stack priority)
( | * 和 / | + 和 - | ) | |
---|---|---|---|---|
isp | 1 | 5 | 3 | 6 |
icp | 6 | 4 | 2 | 1 |
中缀表达式: a + b − a ∗ ( ( c + d ) / e − f ) + g a+b-a*((c+d)/e-f)+g a+b−a∗((c+d)/e−f)+g
待处理序列 | 栈 | 后缀表达式 | 当前扫描元素 | 动作 |
---|---|---|---|---|
a + b − a ∗ ( ( c + d ) / e − f ) + g a+b-a*((c+d)/e-f)+g a+b−a∗((c+d)/e−f)+g | a | a加入到后缀表达式 | ||
+ b − a ∗ ( ( c + d ) / e − f ) + g +b-a*((c+d)/e-f)+g +b−a∗((c+d)/e−f)+g | a | + | +入栈 | |
b − a ∗ ( ( c + d ) / e − f ) + g b-a*((c+d)/e-f)+g b−a∗((c+d)/e−f)+g | + | a | b | b加入到后缀表达式 |
− a ∗ ( ( c + d ) / e − f ) + g -a*((c+d)/e-f)+g −a∗((c+d)/e−f)+g | + | ab | - | icp(’-’)低于isp(’+’),弹出+ |
− a ∗ ( ( c + d ) / e − f ) + g -a*((c+d)/e-f)+g −a∗((c+d)/e−f)+g | ab+ | - | - 入栈 | |
a ∗ ( ( c + d ) / e − f ) + g a*((c+d)/e-f)+g a∗((c+d)/e−f)+g | - | ab+ | a | a加入到后缀表达式 |
∗ ( ( c + d ) / e − f ) + g *((c+d)/e-f)+g ∗((c+d)/e−f)+g | - | ab+a | * | *的优先级高于栈顶的- ,*入栈 |
( ( c + d ) / e − f ) + g ((c+d)/e-f)+g ((c+d)/e−f)+g | -* | ab+a | ( | ( 的优先级高于*,( 入栈 |
( c + d ) / e − f ) + g (c+d)/e-f)+g (c+d)/e−f)+g | -*( | ab+a | ( | icp(’(’)高于isp(’(’),(入栈 |
c + d ) / e − f ) + g c+d)/e-f)+g c+d)/e−f)+g | -*(( | ab+a | c | 加入到后缀表达式 |
+ d ) / e − f ) + g +d)/e-f)+g +d)/e−f)+g | -*(( | ab+ac | + | +入栈 |
d ) / e − f ) + g d)/e-f)+g d)/e−f)+g | -*((+ | ab+ac | d | d加入到后缀表达式 |
) / e − f ) + g )/e-f)+g )/e−f)+g | -*((+ | ab+acd | ) | ( 前面的运算符依次加到表达式中 |
/ e − f ) + g /e-f)+g /e−f)+g | -*( | ab+acd+ | / | /入栈 |
e − f ) + g e-f)+g e−f)+g | -*(/ | ab+acd+ | e | e加入到后缀表达式 |
− f ) + g -f)+g −f)+g | -*(/ | ab+acd+e | - | - 的优先级低于栈顶的/,弹出/ |
− f ) + g -f)+g −f)+g | -*(/ | ab+acd+e/ | - | - 入栈 |
f ) + g f)+g f)+g | -*(- | ab+acd+e/ | f | f加入到后缀表达式 |
) + g )+g )+g | -*(- | ab+acd+e/f | ) | ( 前面的运算符依次加到表达式中 |
+g | -* | ab+acd+e/f- | + | +的优先级低于栈顶的*,弹出* |
+g | - | ab+acd+e/f-* | + | icp(’+’)低于isp(’-’),弹出- |
+g | ab+acd+e/f-*- | + | +入栈 | |
g | + | ab+acd+e/f-*- | g | g加入到后缀表达式 |
+ | ab+acd+e/f-*-g | 扫描完毕,把剩下的运算符依次加入表达式 | ||
ab+acd+e/f-*-g+ | 转换完成 |
通过两道例题,相信能比较好的理解中缀表达式是怎么转换成后缀表达式的了吧
代码实现
#include <iostream>
#include <stack>
#include <unordered_map>
using namespace std;
unordered_map<char, int>isp = {{'(', 1}, {'*', 5}, {'/', 5},
{'+', 3}, {'-', 3}, {')', 6}};
unordered_map<char, int>icp = {{'(', 6}, {'*', 4}, {'/', 4},
{'+', 2}, {'-', 2}, {')', 1}};
/**
* 判断c是否是[a-zA-Z]之间的数
* @param c 需要判断的字符
* @return 是否是操作数
*/
bool op(char c){
if (c >= 97 && c <= 122)
return true;
if (c >= 65 && c <= 90)
return true;
return false;
}
string in2post(string infix){
// 后缀表达式
string postfix;
int inLen = infix.length();
char curChar;
char topCHar;
stack<char> stack;
for (int i = 0; i < inLen; ++i) {
// 获取当前字符
curChar = infix[i];
// 如果是数字则加入到后缀表达式中
if (op(curChar)){
postfix += curChar;
continue;
}
// 如果是运算符,则要进行判断
else{
// 如果栈为空,则直接把运算符压入栈
if (stack.empty())
stack.push(curChar);
else{
topCHar = stack.top();
// 如果当前运算符比栈顶的运算符优先级高,则入栈
if (icp[curChar] > isp[topCHar])
stack.push(curChar);
// 如果当前运算符比栈顶的运算符优先级低并且不是右括号 ) ,则要弹出栈顶的运算符,再入栈
else if(icp[curChar] < isp[topCHar] && curChar != ')') {
do {
postfix += topCHar;
stack.pop();
// 如果栈为空了,直接退出循环
if (stack.empty())
break;
topCHar = stack.top();
}while (icp[curChar] < isp[topCHar]);
stack.push(curChar);
}
// 如果当前运算符是右括号 ),要把括号中的运算符依次弹出。
else{
while (topCHar != '('){
postfix += topCHar;
stack.pop();
topCHar = stack.top();
}
stack.pop();
}
}
}
}
// 待处理的序列已经处理完了,剩下的就是把栈中的运算符依次加到后缀表达式
while (!stack.empty()) {
postfix += stack.top();
stack.pop();
}
return postfix;
}
int main(){
// string infix = "a/b+(c*d-e*f)/g";
string infix = "a+b-a*((c+d)/e-f)+g";
string postfix = in2post(infix);
cout << postfix << endl;
return 0;
}