十、从中缀向后缀转换表达式
题目描述
中缀表达式就是我们通常所书写的数学表达式,后缀表达式也称为逆波兰表达式,在编译程序对我们书写的程序中的表达式进行语法检查时,往往就可以通过逆波兰表达式进行。我们所要设计并实现的程序就是将中缀表示的算术表达式转换成后缀表示,例如,将中缀表达式
(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+/ | 1秒 | 64M | 0 |
测试用例 2 | 2a+b*c-d# a-b*(c+d)# | abc*+d- abcd+*- | 1秒 | 64M | 0 |
解题思路
后缀表达式中不含有括号,且后缀表达式中的操作数顺序和中缀表达式的操作数顺序完全相同,只是运算符的位置改变了。考虑用栈来实现,我们可以设置两个栈,一个 opt
栈用来存放运算符,一个 exp
栈用来存放最后的后缀表达式
在将中缀表达式转换为后缀的时候,遵循的原则是:
- 从左到右依次扫描中缀表达式,如果读到的是操作数,直接存入 exp 栈
- 如果读到的是运算符,则进行判断
- 该运算符是 ’ ( ',则直接存入 opt 栈
- 该运算符是 ’ ) ',则将 opt 栈中对应 ‘(’ 前的所有运算符出栈,存入 exp 栈(这一对括号就可以直接舍弃了)
- 如果该运算符不是括号,则将该运算符和 opt 栈顶运算符做比较
- 优先级大于或等于 opt 栈顶运算符,则直接存入 opt 栈
- 优先级小于 opt 栈顶运算符,则让 opt 栈顶运算符出栈并存入 exp 栈中。如果此时新的栈顶运算符优先级大于等于该运算符,继续让栈顶运算符出栈存入exp栈。最后再将该运算符存入 opt 栈
- 当扫描完成后,opt 栈中还有运算符,则将 opt 所有运算符出栈,存入 exp 栈中
符号的优先级定义如下:
(
优先级为 1+
、-
优先级为 3*
、/
优先级为 3^
优先级为 7)
优先级为 9
对于 测试用例1
的转换过程如下:
扫描字符串 | opt栈 | exp栈 |
---|---|---|
( | ( | 空 |
(A | ( | A |
(A- | (- | A |
(A-( | (-( | A |
(A-(B | (-( | AB |
(A-(B* | (-(* | AB |
(A-(B*C | (-(* | ABC |
(A-(B*C+ | (-(+ | ABC* |
(A-(B*C+D | (-(+ | ABC*D |
(A-(B*C+D) | (- | ABC*D+ |
(A-(B*C+D)* | (-* | ABC*D+ |
(A-(B*C+D)*E | (-* | ABC*D+E |
(A-(B*C+D)*E) | 空 | ABC*D+E*- |
(A-(B*C+D)*E)/ | / | ABC*D+E*- |
(A-(B*C+D)*E)/( | /( | ABC*D+E*- |
(A-(B*C+D)*E)/(F | /( | ABC*D+E*-F |
(A-(B*C+D)*E)/(F+ | /(+ | ABC*D+E*-F |
(A-(B*C+D)*E)/(F+G | /(+ | ABC*D+E*-FG |
(A-(B*C+D)*E)/(F+G) | / | ABC*D+E*-FG+ |
(A-(B*C+D)*E)/(F+G)# | 空 | ABC*D+E*-FG+/ |
上机代码
懒得手写 C 的栈了,我就直接使用了 C ++ 封装好的栈
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
using namespace std;
bool JudgeOpt(char a, char b)
{
//判断两个运算符优先级
int value1 = 0, value2 = 0;
switch(a)
{
case '(': value1 = 1;break;
case '+': value1 = 3;break;
case '-': value1 = 3;break;
case '*': value1 = 5;break;
case '/': value1 = 5;break;
case '^': value1 = 7;break;
case ')': value1 = 9;break;
}
switch(b)
{
case '(': value2 = 1;break;
case '+': value2 = 3;break;
case '-': value2 = 3;break;
case '*': value2 = 5;break;
case '/': value2 = 5;break;
case '^': value2 = 7;break;
case ')': value2 = 9;break;
}
//优先级a<b则false
if(value1 - value2 < 0)
return false;
else
return true;
}
int main()
{
int n = 0;
scanf("%d",&n);
stack<char> opt,exp;
while(n--)
{
//初始化两个栈
while(!opt.empty())
opt.pop();
while(!exp.empty())
exp.pop();
char str[110];
scanf("%s",str);
char ch; //用一个ch来表示str[i]
//求字符串长度
int len = 0;
while(str[len]!='\0')
len++;
//扫描字符串
for(int i = 0; i < len; i++)
{
ch = str[i];
if(ch == '#') //扫描完毕则退出
break;
//操作数直接压栈
if((ch >= 'a'&& ch <= 'z') || (ch >= 'A'&& ch <= 'Z') || (ch >= '0'&& ch <= '9'))
exp.push(ch);
else //判断括号
{
if(ch == '(')//左括号直接压栈
opt.push(ch);
else if(ch == ')')//右括号则取opt中对应的操作符存入exp中
{
while(opt.top()!='(')
{
exp.push(opt.top());
opt.pop();
}
opt.pop();//舍弃栈顶的左括号
}
else //判断运算符
{
if(opt.empty() || JudgeOpt(ch, opt.top()))//优先级大直接存入
{
opt.push(ch);
}
else //优先级小,栈顶元素出栈
{
exp.push(opt.top());
opt.pop();
while(!opt.empty())//继续出栈
{
if(JudgeOpt(opt.top(), ch))
{
exp.push(opt.top());
opt.pop();
}
else
break;
}
opt.push(ch);//最后ch存入opt
}
}
}
}
//将opt剩余运算符存入exp中
while(!opt.empty())
{
exp.push(opt.top());
opt.pop();
}
//取出exp的后缀表达式,逆序输出
int num = 1;
while(!exp.empty())
{
str[num++]=exp.top();
exp.pop();
}
for(int i = num - 1; i > 0; i--)
{
printf("%c", str[i]);
}
printf("\n");
}
return 0;
}