数据结构 - 栈 - 从中缀向后缀转换表达式
用栈来实现中缀表达式向后缀表达式的转换。
从中缀向后缀转换表达式
中缀表达式就是我们通常所书写的数学表达式,后缀表达式也称为逆波兰表达式,在编译程序对我们书写的程序中的表达式进行语法检查时,往往就可以通过逆波兰表达式进行。我们所要设计并实现的程序就是将中缀表示的算术表达式转换成后缀表示,例如,将中缀表达式
(A-(B*C+D)*E)/(F+G)
转换为后缀表示为:ABC*D+E*-FG+/
。注意:为了简化编程实现,假定变量名均为单个字母,运算符只有
+
,-
,*
,/
和^
(指数运算),可以处理圆括号()
,并假定输入的算术表达式正确。要求:使用栈数据结构实现 ,输入的中缀表达式以
#
号结束。
输入格式
-
整数 \(N\),表示下面有 \(N\) 个中缀表达式。
-
\(N\) 个由单个字母和运算符构成的表达式。
输出格式
- \(N\) 个后缀表达式。
输入输出样例
样例 1
输入
1
(A-(B*C+D)*E)/(F+G)#
输出
ABC*D+E*-FG+/
样例 2
输入
2
a+b*c-d#
a-b*(c+d)#
输出
abc*+d-
abcd+*-
注意输入输出的每一行都有换行符\n
。
题解
考虑此问题时,先将问题进行简化。比如假设一个表达式中只要运算符,没有左右括号()
,此时我们只需对运算符以优先级的高低维护一个单调栈(栈中运算符元素严格单调递增)。
考虑括号时,对左右括号按括号匹配的思路,在括号对的内部对运算符进行的单调栈维护。每次匹配到一对括号就在进行下一次操作前,就将此对括号内的所有运算符全部归约完即可(一次归约即一次两个操作数与一个操作符的匹配过程)。
当然还有另一种思路,就是将括号本身视为运算符,为左括号(
赋予最低优先级,为右括号)
赋予最高优先级,也可以完成中缀表达式向后缀表达式的转换。
为清晰期间,我们使用前一种思路,即对左右括号按括号匹配的同时,在括号对内部对运算符进行单调栈维护。
题解代码
代码如下:
#include <cstdio>
#include <cstdlib>
#include <stack>
#include <iostream>
using namespace std;
bool isalpha(char ch)
{
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) return true;
else return false;
}
int order(char op)
{
if (op == '+' || op == '-') return 1;
if (op == '*' || op == '/') return 2;
if (op == '^') return 3;
}
int main()
{
// 不考虑括号时,只需对运算符以优先级维护一个单调栈(栈中元素严格单调递增)
// 考虑括号时,对左右括号按括号匹配的思路,在括号对内部对运算符进行的单调栈维护
// 将一对括号对视为一个字母变量
int n; cin >> n;
while (n--) {
char ch;
stack<char> op;
ch = getchar();
while ((ch = getchar()) && ch != '#') {
if (isalpha(ch)) {
printf("%c", ch);
}
else if (ch == ')') {
// 匹配到右括号,直接将这对括号内的所有运算都进行归约
while (!op.empty() && op.top() != '(') {
char tmp = op.top();
op.pop();
printf("%c", tmp);
}
if (!op.empty()) op.pop();
}
else if (ch == '(') {
op.push('(');
}
else {
// 维持栈的严格单调递增性质
// while ((!op.empty() && op.top() != '(') && (((op.top() == ch) && (ch == '^')) ? (order(op.top()) > order(ch)) : (order(op.top()) >= order(ch)))) {
while (!op.empty() && op.top() != '(') {
if (order(op.top()) >= order(ch)) {
printf("%c", op.top());
op.pop();
}
else break;
}
op.push(ch);
}
}
while (!op.empty()) {
printf("%c", op.top());
op.pop();
}
printf("\n");
}
return 0;
}
这里提醒一点,我完成此题是在北理的乐学网站上进行的提交,上述代码会有两个测试用例无法通过,后面我通过在网上查找到了其他人写的能通过的代码,通过与我自己的代码比对,发现是指数运算的问题。当给定的输入为
1
a^b^c^d#
通过代码的输出为
abcd^^^
我自己的代码的输出为
ab^c^d^
但根据后缀表达式的定义(也许是我搞错了?),貌似后一个输出才是正确的。也就是说北理的那道题的给的“正确输出”可能有问题,导致有两个测试用例(应该是关于指数运算的)通过不了。能通过北理测试用例的代码如下:
#include <cstdio>
#include <cstdlib>
#include <stack>
#include <iostream>
using namespace std;
bool isalpha(char ch)
{
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) return true;
else return false;
}
int order(char op)
{
if (op == '+' || op == '-') return 1;
if (op == '*' || op == '/') return 2;
if (op == '^') return 3;
}
int main()
{
// 对运算符以优先级维护一个单调栈(栈中元素严格单调递增,除非有左右括号规定运算顺序)
int n; cin >> n;
while (n--) {
char ch;
stack<char> op;
ch = getchar();
while ((ch = getchar()) && ch != '#') {
if (isalpha(ch)) {
printf("%c", ch);
}
else if (ch == ')') {
// 匹配到右括号,直接将这对括号内的所有运算都进行归约
while (!op.empty() && op.top() != '(') {
char tmp = op.top();
op.pop();
printf("%c", tmp);
}
if (!op.empty()) op.pop();
}
else if (ch == '(') {
op.push('(');
}
else {
while ((!op.empty() && op.top() != '(') && (((op.top() == ch) && (ch == '^')) ? (order(op.top()) > order(ch)) : (order(op.top()) >= order(ch)))) {
//while (!op.empty() && op.top() != '(') {
//if (order(op.top()) >= order(ch)) {
printf("%c", op.top());
op.pop();
//}
//else break;
}
op.push(ch);
}
}
while (!op.empty()) {
printf("%c", op.top());
op.pop();
}
printf("\n");
}
return 0;
}