一、题目
Infix to Postfix Conversion
Convert the infix expression to postfix expression.
输入格式:
1 line.
A correct expression include +,-,*,/,(,) and integer. Each integer is less than 10 and more than 0.输出格式:
2 line.
The first line gives the result of expression. Keep 2 digits after the decimal point.
The second line gives the postfix expression. Use a blank space to divide every number and symbol. There is a blank space at the end of the line.
输入样例:
在这里给出一组输入。例如:
2*(3+4)/5
输出样例:
在这里给出相应的输出。例如:
2.80
2 3 4 + * 5 /
一、概念
中缀表达式:平时熟悉的表达式,如2*(3+4)/5
后缀表达式:符合计算机处理算式的逻辑,由运算数和运算符组成。计算顺序严格从左到右,运算符位于对应的两个运算数之后。遍历后缀表达式时,遇到运算符则对其前两个数进行该运算符要求的操作,然后删掉这三项,保留结果在这三项原来的位置,继续后面的计算。
二、实现思路
(我是完全用数组完成的,没有涉及链表。写起来比较简洁,但是内存使用不灵活。)
首先建立两个个数组:
(1)用于读取中缀表达式(代码中的s[100],即string简写)
(2)用于记录后缀表达式的结果方便打印(代码中的r[50],即result简写)
一个栈(其实还是数组):用于中缀表达式转后缀表达式的过程操作,只存运算符(代码中的o[50],即operate简写)
1、中缀表达式转后缀表达式
从左到右遍历中缀表达式字符串,依据当前字符进行下面的操作
(1)运算数:直接存入后缀表达式
(2)左括号:存入栈,优先级排在最低
(3)右括号:不用存到栈中,只需在栈中从栈顶开始依次取出运算符,将它们依次存入后缀表达式中,直到遇见左括号。左括号也要取出,但不需要存入后缀表达式。
(4)运算符:分为三种情况,最终都是入栈
——[1]栈为空:直接入栈
——[2]栈非空,且栈顶运算符优先级比当前运算符低:直接入栈
——[3]栈非空,且栈顶运算符优先级比当前运算符高:在栈中从栈顶开始依次取出运算符并存到后缀表达式中,直到栈顶运算符优先级比当前运算符低,再将其入栈。
遍历完成后,判断栈是否为空,非空则一次性把栈中剩余的运算符从栈顶开始全部取出依次存入后缀表达式中。
此时把后缀表达式打印出来就是结果。
2、用后缀表达式进行计算
首先建立一个栈(数组),用于计算过程中存储后缀表达式中的数以及某步运算的结果(代码中的num[50])。
从左到右遍历后缀表达式。遇到数则将其存到栈中(注意从char到float强制类型转换之后根据ascii码要减48)。遇到运算符则将栈顶的两个数取出,进行计算,然后将这一步的运算结果存入栈(这一步要注意两个数减法和除法的顺序问题)。
全部遍历完之后,栈中只剩下一个数,即为结果。
三、我的代码(C语言)
#include<stdio.h>
void PopBracket(char *o,int *top,char *r,int *j){
while(o[*top]!='('){
r[(*j)++]=o[(*top)--];
}
(*top)--;
return;
}
int compare(char x,char y,int top){
if(top==-1){
return 0;
}
else if(y=='('){
return 0;
}
else if((x=='*'||x=='/')&&(y=='+'||y=='-')){
return 0;
}
else{
return 1;
}
}
void calculate(float *num,char sign,int *top){
float a=num[(*top)--];
float b=num[(*top)--];
float result=0;
if(sign=='+') result=a+b;
else if(sign=='-') result=b-a;
else if(sign=='*') result=a*b;
else if(sign=='/') result=b/a;
else{
printf("error");
return;
}
num[++(*top)]=result;
return;
}
int main(){
char s[100],o[50],r[50];
int top=-1;
scanf("%s",s);
int i,j=0;
for(i=0;s[i]!='\0';i++){
if(s[i]>=48&&s[i]<=57){
r[j++]=s[i];
}
else if(s[i]=='('){
o[++top]='(';
}
else if(s[i]==')'){
PopBracket(o,&top,r,&j);
}
else{
if(top==-1){
o[++top]=s[i];
}
else{
while(compare(s[i],o[top],top)){
r[j++]=o[top--];
}
o[++top]=s[i];
}
}
}
while(top!=-1){
r[j++]=o[top--];
}
float num[50];
top=-1;
for(i=0;i<j;i++){
if(r[i]>=48&&r[i]<=57){
num[++top]=(float)r[i]-48;
}else{
calculate(num,r[i],&top);
}
}
printf("%.2f\n",num[0]);
for(i=0;i<j;i++){
printf("%c ",r[i]);
}
printf("\n");
return 0;
}
四、一些问题
我自己写代码的时候,在“计算后缀表达式”这一步出了很多问题。我一开始想的是把后缀表达式转换成链表(数和运算符都在),然后用链表进行计算的过程。方法为:遍历链表,遇到运算符则对其前两个结点代表的数进行计算,然后删除这三个结点,把代表结果的新结点放到原来这三个结点的位置上,然后继续遍历链表。直到遍历完成,最终剩下的结点中的数即为运算结果。在这样写的过程中我遇到了很多问题。
由于整个后缀表达式中有些是数有些是运算符,数必须是float类型以便计算,运算符又不好转换成float类型(或者强制转换成某个数的话容易无法实现上面的功能),所以我在结构体中设置了两个能存储数据的标记(float data和char opr),所有的数都是opr=' ',所有的运算符都是data=-1。这样,遍历的时候只需检查data是否是-1就能知道是不是运算符。
由于遇到运算符时要往回看数,所以我把整个链表设定成了双向的链表。在遇到运算符时,需要往回看前面三项,两项为参与运算的数,一项为这两个数前面的结点,用于与新结点建立连接。为了避免指针出错,我在整个后缀表达式链表的前后分别加了一个头结点和一个尾结点,这样,在最后一步运算之前(链表为头->数->数->运算符->尾)还能保证每个结点都有各自的指向(最靠前的指针指向运算符,后面有三个位置给指针往回看)。每次从负责这一功能的函数返回回来,当前指针都是指向新生成的结点的,所以最后只剩下一个数的时候指针应该也没出错。
写完终于在我自己电脑上能运行成功了,算出来的结果也是正确的,但是放到PTA上显示只对了75%,剩下25%扣在了“段错误”上,但我始终找不到错在哪了,后来才换方法。新方法确实比我最开始想的要简洁明了很多。希望之后我能找到到底错在哪了,也希望看到的朋友给出建议~~~