逆波兰记法中,操作符置于操作数的后面。例如表达“三加四”时,写作“3 4 +”,而不是“3 + 4”。如果有多个操作符,操作符置于第二个操作数的后面,所以常规中缀记法的“3 - 4 + 5”在逆波兰记法中写作“3 4 - 5 +”:先3减去4,再加上5。使用逆波兰记法的一个好处是不需要使用括号。例如中缀记法中“3 - 4 * 5”与“(3 - 4)*5”不相同,但后缀记法中前者写做“3 4 5 * -”,无歧义地表示“3 (4 5 *) −”;后者写做“3 4 - 5 *”。
逆波兰表达式的解释器一般是基于堆栈的。解释过程一般是:操作数入栈;遇到操作符时,操作数出栈,求值,将结果入栈;当一遍后,栈顶就是表达式的值。因此逆波兰表达式的求值使用堆栈结构很容易实现,并且能很快求值。
注意:逆波兰记法并不是简单的波兰表达式的反转。因为对于不满足交换律的操作符,它的操作数写法仍然是常规顺序,如,波兰记法“/ 6 3”的逆波兰记法是“6 3 /”而不是“3 6 /”;数字的数位写法也是常规顺序。
写这个博客只是因为感觉自己遇到的资料对逆波兰表达式的说明都不是很详细……
然后自己写一下把中缀记法的算式变成后缀记法的思路和代码……
思路:观察后我们不难发现,逆波兰式其实就是用运算符的出现顺序代表了这些运算的优先级,在一个逆波兰表达式中,最先出现的运算符最先进行运算,后出现的运算符后运算,每个运算符运算时就是取其前面的两个数进行运算,运算结束后把结果放回栈中,等待下一次运算。所以我们把中缀变为后缀,其实就是在做一件事:
运算优先级的划分。
所以简述一下代码要做的事情:
- 遇到数字,直接将其放入结果的末端
- 遇到运算符,如果栈内无比其优先级高或相等的运算符,就直接入栈(因为栈的先进先出,越靠近栈顶的运算符优先级越高);如果栈内有比其优先级高或相等的运算符,就把这些运算符出栈,放入结果的末端(在结果越前面优先级越高,因为运算的优先级和左结合律,之前的运算符都要出栈)
- 遇到括号,括号内的优先级是最高的,如果是‘(’就先入栈(括号内的计算还没开始),如果是‘)’,就开始把栈内的运算符拿出来,一个个放入结果的末端,直到遇到‘(’
- 结束时把栈内的运算符拿出来,一个个放入结果的末端
就酱
#include <iostream>
#include <stack>
#include <string>
#include <cctype>
using namespace std;
//用一个内联函数来判断运算符的优先级
inline int priority(char x) {
switch (x) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0;
}
}
string RPN(const string &infix) {
stack<char> s;
string ret;
//用来标记前一个字符是否也是数字,若当前字符不是数字但前一个是
//那么就要在ret的后面加上空格,以区分数字
bool digit = false;
for (auto ch: infix) {
if (isdigit(ch) || ch == '.') {
if (!digit) {
ret.push_back(' ');
digit = true;
}
ret.push_back(ch);
} else {
if (digit) {
digit = false;
ret.push_back(' ');
}
if (ch == '(') {
s.push(ch);
} else if (ch == ')') {
while (s.top() != '(') {
ret.push_back(s.top());
s.pop();
}
s.pop();
} else {
while (!s.empty() && priority(s.top()) >= priority(ch)) {
ret.push_back(s.top());
s.pop();
}
s.push(ch);
}
}
}
ret.push_back(' ');
while (!s.empty()) {
ret.push_back(s.top());
s.pop();
}
return ret;
}
int main() {
string infix;
cin >> infix;
cout << RPN(infix);
}