一、后缀表达式计算
后缀表达式,也被称为逆波兰表示法(Reverse Polish Notation,简称RPN),是一种不需要括号来标识操作符优先级的数学表达式。在后缀表达式中,所有的操作符都跟随在它们的操作数之后,这使得它的结构比标准的算术表达式更为简洁明了,尤其是在进行复杂计算时。其优点主要在于:
- 消除了表达式中的括号:后缀表达式不需要括号来指定操作的顺序,因为操作符的位置已经明确了操作的顺序。
- 易于计算机计算:后缀表达式可以很容易地用栈结构在计算机程序中实现,这使得它在编程语言的解释器和编译器中特别有用。
- 消除了优先级规则的需求:由于计算顺序完全由操作符的位置决定,因此不需要考虑运算符优先级规则。
1.基本结构
后缀表达式的基本规则是:操作数 操作数 操作符
。例如,传统算术表达式 3 + 4 3 + 4 3+4 在后缀表达式中表示为 34 + 3 4 + 34+。
2.计算方式
要计算后缀表达式的值,需要使用一个栈(Stack):
- 从左至右扫描表达式。
- 遇到数字时,将其推入栈中。
- 遇到操作符时,从栈中弹出两个元素(注意:第一个弹出的元素是右操作数,第二个是左操作数),执行相应的运算,并将结果推回栈中。
- 继续执行,直到整个表达式被处理完。
- 最后,栈中剩余的元素就是表达式的结果。
3.示例
传统表达式与后缀表达式的对比:
- 中缀(传统)表达式: ( 3 + 4 ) × 5 (3 + 4) \times 5 (3+4)×5
- 后缀表达式: 34 + 5 × 3 4 + 5 \times 34+5×
计算过程如下:
- 推入3
- 推入4
- 遇到+操作符,弹出4和3,计算3+4=7,将7推回栈
- 推入5
- 遇到×操作符,弹出5和7,计算 7 × 5 = 35 7×5=35 7×5=35,将35推回栈
最后,栈中仅剩的数值35就是该后缀表达式的结果。
二、中缀表达式转后缀表达式
中缀表达式是大多数人习惯的数学表达式写法,其中运算符位于两个操作数之间,如 A + B A + B A+B。相比之下,后缀表达式(也称为逆波兰表示法)中,运算符位于操作数之后,例如 A B + A B + AB+。
将中缀表达式转换为后缀表达式可以使表达式更易于计算机程序处理,因为后缀表达式不需要括号来指定运算顺序。转换过程通常使用一个栈来辅助.
1.具体步骤:
- 创建一个空栈:用于存放运算符(包括括号),以及一个输出列表用于构建后缀表达式。
- 从左到右扫描中缀表达式:
- 如果遇到操作数,直接将其添加到输出列表。
- 如果遇到左括号,将其压入栈中。
- 如果遇到右括号,则从栈中弹出运算符并添加到输出列表,直到遇到左括号(左括号弹出但不添加到输出列表)。
- 如果遇到运算符,从栈中弹出所有优先级更高或相等的运算符,并将它们添加到输出列表中,然后将当前运算符压入栈中。
- 扫描完成后,从栈中弹出所有剩余的运算符并添加到输出列表。
优先级和结合性规则决定了操作符从栈中弹出的时机。在比较运算符优先级时,乘法和除法通常优先于加法和减法。当有多个运算符具有相同的优先级时,它们的结合性决定了它们的
2.示例
将中缀表达式 A * (B + C) / D
转换为后缀表达式:
- 扫描
A
,因为它是操作数,直接输出A
。 - 扫描
*
,将其压入栈中。 - 扫描
(
,将其压入栈中。 - 扫描
B
,因为它是操作数,直接输出B
。 - 扫描
+
,将其压入栈中(注意,尽管+
的优先级低于*
,但由于+
在括号内,我们会暂时忽略外部的运算符)。 - 扫描
C
,因为它是操作数,直接输出C
。 - 扫描
)
,开始弹出栈中的运算符直到遇到(
,因此输出+
,然后丢弃(
。 - 最后,扫描
/
,弹出并输出栈顶的*
(因为*
的优先级与/
相等),然后将/
压入栈。
此时,输出列表为 A B C + ∗ A B C + * ABC+∗,然后把 /
添加到输出列表(因为栈内不再有其他运算符),所以最终的后缀表达式为 A B C + ∗ / A B C + * / ABC+∗/。
三、代码实现
#include <iostream>
#include <stack>
#include <string>
using namespace std;// 检查字符是否是操作符
bool isOperator(char c) {return c == '+' || c == '-' || c == '*' || c == '/';
}// 检查运算符的优先级
int getPriority(char c) {if (c == '*' || c == '/') return 2;if (c == '+' || c == '-') return 1;return 0;
}// 中缀表达式转后缀表达式
string infixToPostfix(const string& infix) {string postfix;stack<char> opStack;for (char c : infix) {if (c == ' ') continue; // 忽略空格if (isdigit(c)) {postfix += c;}else if (c == '(') {opStack.push(c);}else if (c == ')') {while (!opStack.empty() && opStack.top() != '(') {postfix += opStack.top();opStack.pop();}opStack.pop(); // 弹出 '('}else if (isOperator(c)) {while (!opStack.empty() && getPriority(opStack.top()) >= getPriority(c)) {postfix += opStack.top();opStack.pop();}opStack.push(c);}}// 将栈内剩余的操作符添加到后缀表达式while (!opStack.empty()) {postfix += opStack.top();opStack.pop();}return postfix;
}// 计算后缀表达式
int calculatePostfix(const string& postfix) {stack<int> valStack;for (char c : postfix) {if (isdigit(c)) {valStack.push(c - '0'); // 将字符转换为整数}else {int right = valStack.top(); valStack.pop();int left = valStack.top(); valStack.pop();switch (c) {case '+': valStack.push(left + right); break;case '-': valStack.push(left - right); break;case '*': valStack.push(left * right); break;case '/': valStack.push(left / right); break;}}}return valStack.top();
}int main() {string infix;cout << "Enter infix expression: ";getline(cin, infix); // 读取整行作为中缀表达式string postfix = infixToPostfix(infix);cout << "Postfix expression: " << postfix << endl;int result = calculatePostfix(postfix);cout << "Result: " << result << endl;return 0;
}