前缀表达式,中缀表达式,后缀表达式
概念
- 前缀表达式:波兰表示法:操作符置于操作数前面(
+ab
) - 中缀表达式:操作符置于操作数之间(
a+b
) - 后缀表达式:逆波兰表示法:操作符置于操作数后面(
ab+
)
核心思想:操作符置于操作数的位置。当中缀需要转换时,因为运算符的位置改变,需要先将运算符暂存起来,使用栈结构
中缀转后缀
- 从左到右扫描表达式
- 遇到操作数直接输出,输出顺序从左到右
- 遇到操作符
- 操作符为:
“(”
/优先级>栈顶符号
/栈为空
⇒ 入栈 - 操作符为:
优先级<=栈顶符号
⇒ 栈顶出栈。并再次与栈顶比较
- 操作符为:
- 遇到括号
- 括号为:
”(”
⇒ 入栈 - 括号为:
”)”
⇒ 依次弹出栈顶符号,直到遇到“(”
,然后丢弃这一对括号
- 括号为:
- 将栈中剩余的操作符依次弹出,输出顺序从左到右
中缀转前缀
- 从右到左扫描表达式
- 遇到操作数直接输出,输出顺序从右到左
- 遇到操作符
- 操作符为:
“)”
/优先级>=栈顶符号
/栈为空
⇒ 入栈 - 操作符为:
优先级<栈顶符号
⇒ 栈顶出栈。并再次与栈顶比较
- 操作符为:
- 遇到括号
- 括号为:
”)”
⇒ 入栈 - 括号为:
”(”
⇒ 依次弹出栈顶符号,直到遇到“)”
,然后丢弃这一对括号
- 括号为:
- 将栈中剩余的操作符依次弹出,输出顺序从右到左
做题方法
(1)要有整体的感觉
在解这类问题时,需要有整体的感觉,a+b
把这里的a和b看作是一个表达式,作为一个整体,比如a*b+c*d
,就是把a*b
看作一个整体,把c*d
看作一个整体。
例如:假如有括号形式,(a+b+c)*d
这里的整体就是(a+b+c)
和d
。
- 将前面这一部分设为A,写成后缀表达式就是
Ad*
; - 然后再将A这一个整体继续拆
a+b+c
,运算顺序是先a+b再+c,所以先将a+b看作一个整体B,写为后缀表达式为Bc+
; - 最后将a+b写为
ab+
最终结果就是ab+c+d*
对于简单的问题,可以眼瞅出来做的,但对于复杂一些的问题,就不是这么容易了,就需要用更加系统的方法
(2)中缀转后缀的操作步骤
例如:a+b-a*((c+d)/e-f)+g
(2012年408真题),将这个中缀表达式转换为后缀表达式,手写需要写两行:输出,栈。一个写当前输出的内容,一个写栈中存储的内容。主要的原则就是按照上面介绍的中缀转后缀步骤,一步步操作,多做几次就熟练了,下面详细写一个每步操作的介绍。
当然,自己在草稿纸上手写的话,就可以捋着中缀表达式来,心里清楚对于扫描到的每个元素是该怎么操作就行
- 从左到右扫
a+b-a*((c+d)/e-f)+g
步骤 | 当前符号 | 栈内符号 | 操作判断 | 当前操作 | 输出结果 |
---|---|---|---|---|---|
1 | a | 空 | 操作数→直接输出 | 输出a | a |
2 | + | 空 | 操作符→栈空→入栈 | +入栈 | a |
3 | b | + | 操作数→直接输出 | 输出b | ab |
4 | - | + | 优先级-=+→+出栈 | 输出+ | ab+ |
- | 空 | 操作符→栈空→入栈 | -入栈 | ab+ | |
5 | a | - | 操作数→直接输出 | 输出a | ab+a |
6 | * | - | 优先级*>-→*入栈 | *入栈 | ab+a |
7 | ( | -* | 左括号→入栈 | (入栈 | ab+a |
8 | ( | -*( | 左括号→入栈 | (入栈 | ab+a |
9 | c | -*(( | 操作数→直接输出 | 输出c | ab+ac |
10 | + | -*(( | 栈顶为左括号→入栈 | +入栈 | ab+ac |
11 | d | -*((+ | 操作数→直接输出 | 输出d | ab+acd |
12 | ) | -*((+ | 右括号→出栈,直到遇到( | 输出+ | ab+acd+ |
) | -*(( | 遇到(,丢弃这对括号 | (出栈,并丢弃 | ab+acd+ | |
13 | / | -*( | 栈顶为左括号→入栈 | /入栈 | ab+acd+ |
14 | e | -*(/ | 操作数→直接输出 | 输出e | ab+acd+e |
15 | - | -*(/ | 优先级-</→/出栈 | 输出/ | ab+acd+e/ |
- | -*( | 栈顶为左括号→入栈 | -入栈 | ab+acd+e/ | |
16 | f | -*(- | 操作数→直接输出 | 输出f | ab+acd+e/f |
17 | ) | -*(- | 右括号→出栈,直到遇到( | 输出- | ab+acd+e/f- |
) | -*( | 遇到(,丢弃这对括号 | (出栈,并丢弃 | ab+acd+e/f- | |
18 | + | -* | 优先级+<→出栈 | 输出* | ab+acd+e/f-* |
+ | - | 优先级-=+→-出栈 | 输出- | ab+acd+e/f-*- | |
+ | 空 | 操作符→栈空→入栈 | +入栈 | ab+acd+e/f-*- | |
19 | g | + | 操作数→直接输出 | 输出g | ab+acd+e/f-*-g |
20 | 空 | + | 将栈中剩余元素依次弹出 | 输出+ | ab+acd+e/f-*-g+ |
配合着这个步骤表进行理解,不要生搬硬套这个表,要以前面讲的具体操作步骤来做
代码
#include<iostream>
#include<stack>
#include<string>
#include<algorithm>
#include<map>
using std::stack;
using std::cout;
using std::endl;
using std::string;
using std::map;
// 定义运算符优先级,'('要压入栈,比较优先级,所以应该添加进来
map<char, int> prior = { {'*',3 }, {'/',3}, {'+',2}, {'-',2},{'(',1} };
string trans(const string& mid_str)
{
string end_str{ "" };
stack<char> s;
std::for_each(std::begin(mid_str), std::end(mid_str),
[&](const char c) {
// 操作数->直接输出
if (prior.find(c) == prior.end() && c != ')') {
end_str += c;
return;
}
// 操作符->和栈顶比较
else if (prior.find(c) != prior.end()) {
// 操作符优先级>栈顶 -> 入栈
// 栈为空 -> 入栈
// 栈顶为'(' -> 入栈
// 操作符为'(' -> 入栈
if (s.empty() || prior[c] > prior[s.top()] || c == '(') {
s.push(c);
return;
}
// 操作符优先级<=栈顶 -> 出栈,并再次比较
while (prior[c] <= prior[s.top()]) {
end_str += s.top();
s.pop();
if (s.empty() || prior[c] > prior[s.top()]) {
s.push(c);
return;
}
}
}
// 右括号->出栈,直到遇到')',并丢弃这对括号
else if (c == ')') {
while (s.top() != '(') {
end_str += s.top();
s.pop();
}
s.pop(); // 丢弃左括号
return;
}
}); // 扫描完整个串
// 扫描完字符串,弹出剩余元素
while (!s.empty()) {
end_str += s.top();
s.pop();
}
return end_str;
}
int main()
{
string mid_str = "a+b-a*((c+d)/e-f)+g";
cout << "转换前的中缀表达式: " << mid_str << endl;
auto res = trans(mid_str);
cout << "转换后的后缀表达式: " << res << endl;
}