一、设计思想(一)先将输入的中缀表达式转为后缀再计算的设计思想我们所熟知的计算表达式为中缀表达式,这之中包含运算符的优先级还有括号,这对我们来说已经习以为常了,但是在计算机看来,这是非常复杂的一种表达式。
因此我们需要有一种更能使计算机理解的不用考虑优先级也不包括括号的表达式,也就是后缀表达式。
我们可以借助栈将其实现。
首先,我们需要将中缀表达式转换为后缀表达式,这也是这个算法的关键之处。
我们将创建两个栈,一个是字符型的,用来存放操作符;另一个是浮点型的,存放操作数。
接着,开始扫描输入的表达式,如果是操作数直接进入一个存放后缀表达式的数组,而操作符则按照优先级push进栈(加减为1,乘除为2),若当前操作符优先级大于栈顶操作符优先级或栈为空,push进栈,而当其优先级小于等于栈顶操作符优先级,则从栈内不断pop出操作符并进入后缀表达式数组,直到满足条件,当前操作符才能push 进栈。
左括号无条件入栈,右括号不入栈,而不断从栈顶pop出操作符进入后缀表达式数组,直到遇到左括号后,将其pop出栈。
这样当扫描完输入表达式并从操作符栈pop 出残余操作符后并push进栈,后缀表达式数组中存放的就是我们所需要的后缀表达式了。
扫描后缀表达式数组,若是操作数,将其转换为浮点型push进数栈;若是操作符,则连续从数栈中pop出两个数做相应运算,将结果push进数栈。
当扫描完数组后,数栈顶便为最终结果,将其pop出,输出结果。
(二)一边扫描一边计算的设计思想由于第一种算法需要进行两遍扫描,因此在性能上不会十分优秀。
而此种算法只用扫描一遍,当扫描完输入的表达式后便可以直接输出最终结果。
是第一种算法的改进版,性能上也得到提升,与第一种算法所不同的是其需要同时使用两个栈,一个操作符栈,一个数栈。
当扫描表达式时,若是操作数则将其转换为浮点型后直接push进数栈,而若是操作符则按照优先级规则push进操作符栈(加减为1,乘除为2),若当前操作符优先级大于栈顶操作符优先级或栈为空,push进栈,而当其优先级小于等于栈顶操作符优先级,则从栈内不断pop出操作符,直到满足条件,当前操作符才能push进栈。
左括号无条件入栈,右括号不入栈,而不断从栈顶pop出操作符,直到遇到左括号后,将其pop出栈。
这中间pop出操作符后直接从数栈中pop出两个数并计算,将结果push进数栈。
括号的处理与第一个算法相同。
扫描完成后,从操作符栈pop出残余操作符,从数栈中pop出两个数并计算并进行计算,将结果push进数栈。
数栈顶便为最终结果,将其pop出,输出结果。
两种算法各有各的优缺点,第一种算法过程比较清晰,使我们能够更加容易理解栈的使用规则,但是其性能不如第二种。
第二种算法相比第一种来说性能提高了,但是理解起来就不如第一种那么清晰了。
二、算法流程图以下是先将输入的中缀表达式转为后缀再计算算法:图1 中缀转后缀算法流程图以下是一边扫描一边计算算法:图2一边扫描一边计算算法流程图三、源代码以下为先将输入的中缀表达式转为后缀再计算算法代码:#include "stdio.h"#include "stdlib.h"/*定义存放操作符的结构体*/struct OpStrack{char op[100];/*存放操作符的数组*/int top;/*栈顶索引*/int level[100];/*存放操作符优先级的数组*/}OpStrackArray;/*定义存放语素的结构体*/struct OdStrack{float od[100];/*存放操作数的数组*/int top;/*栈顶索引*/}OdStrackArray;/*声明需要用到的函数*/int judge(char,char[],int,int);int stackIsEmpty();void pushOp(char,int);char popOp();void pushOd(float);float popOd();void compute(char);/*主函数*/void main(){char data[100];/*声明存放中缀表达式的字符串*/char tempdata[200];/*声明存放后缀表达式的字符串*/int i=0;/*作为遍历字符串的索引*/int j=0;/*作为输出后缀表达式的索引*/int z=0;/*作为存放后缀表达式的索引*/int k=0;/*作为将后缀表达式赋给临时转float的索引*/int eq=0;/*判断括号是否正确的条件*/float result;/*声明存放最终结果的float*/scanf("%s",data);/*输入中缀表达式*//*中缀转后缀,并将结果放入tempdata*/while(data[i]!='\0'){if((data[i]>='0'&&data[i]<='9')||data[i]=='.')/*如果是语素则存放至temp数组*/{tempdata[z]=data[i];z++;}else{switch(data[i]){case '+':z+=judge(data[i],tempdata,i,1);break;case '-':z+=judge(data[i],tempdata,i,1);/*加减优先级为1*/break;case '*':z+=judge(data[i],tempdata,i,2);/*乘除优先级为2*/break;case '/':/*返回出栈操作符的数量,以便将z索引向后推相应步*/z+=judge(data[i],tempdata,i,2);break;case '(':pushOp(data[i],-1);/*'('直接入栈,但入栈后优先级最小*/eq++;/*有一个'('eq+1*/break;case ')':while(OpStrackArray.op[OpStrackArray.top-1]!='(')/*')'不入栈*/{/*不断弹出操作符并进入后缀表达式数组,直到遇到'('*/tempdata[z]=popOp();z++;/*索引+1*//*如果发现还没碰到与之匹配的'('时栈已空则说明表达式不合法*/ if(stackIsEmpty()==1){printf("Expression is not valid!Press any key to continue...");getch();return;}}popOp();/*弹出'('*/eq--;/*如有与'('配队的')'则eq-1*/break;}}i++;}if(eq!=0)/*如果eq!=0说明'('与')'不能完全配队,输入的表达式不合法*/{printf("Expression is not valid!Press any key to continue...");getch();return;}/*将操作符栈内存留的操作符放入tempdata*/while(stackIsEmpty()==0){/*如果操作符栈不空则不断弹出操作符并进入后缀表达式数组*/tempdata[z]=popOp();z++;}/*扫描后缀表达式,计算后放入操作数栈*/while(k<z){char temp[20]={'\0'};/*声明临时的存放操作数的数组并附初值*/int t=0;/*作为temp数组的索引*/float f;/*存放转换为float的数*//*如果是语素则存放至temp数组*/while((tempdata[k]>='0'&&tempdata[k]<='9')||tempdata[k]=='.'){temp[t]=tempdata[k];k++;t++;}if(temp[0]!='\0')/*判断temp数组是否为空,是则将其转换为float并压栈*/ {f=atof(temp);/*字符串转float*/pushOd(f);/*操作数入栈*/}if(tempdata[k]=='+'||tempdata[k]=='-'||tempdata[k]=='*'||tempdata[k]=='/'){compute(tempdata[k]);/*如果扫描到操作符则作相应计算*/ }k++;}/*输出后缀表达式*/printf("The postfix expression is:");for(j=0;j<z;j++){printf("%c",tempdata[j]);}printf("\n");/*从操作数栈内弹出最终结果并输出*/result=popOd();printf("The result is:%.2f",result);/*结果取两位小数*/printf("\n");printf("Press any key to continue...");getch();}/*判断操作符优先级并作出相应处理,返回出栈操作符数量*/int judge(char op,char tempdata[],int index,int level){int cont=0;if(stackIsEmpty()==1||OpStrackArray.level[OpStrackArray.top-1]<level){/*如果栈为空或栈顶操作符优先级小于当前操作符优先级则将其压栈*/pushOp(op,level);cont++;/*使z索引向后推1*/}else{while(stackIsEmpty()!=1&&OpStrackArray.level[OpStrackArray.top-1]>=level){/*操作符不断出栈并入后缀表达式数组直到栈为空或栈顶操作符优先级大于等于当前操作符优先级*/tempdata[index]=popOp();index++;/*后缀表达式数组索引+1*/cont++;/*出栈操作符数量+1*/}pushOp(op,level);/*将当前操作符压栈*/}return cont;}/*判断操作符栈是否为空,是则返回1,否返回0*/int stackIsEmpty(){if(OpStrackArray.op[0]=='\0'){return 1;}return 0;}/*将操作符压入栈*/void pushOp(char op,int level){OpStrackArray.op[OpStrackArray.top]=op;/*操作符进入数组*/OpStrackArray.level[OpStrackArray.top]=level;/*为对应操作符附优先级*/ OpStrackArray.top++;/*top索引+1*/}/*操作符出栈,返回栈顶操作符*/char popOp(){char c=OpStrackArray.op[OpStrackArray.top-1];/*取得栈顶操作符*/OpStrackArray.op[OpStrackArray.top-1]='\0';/*分别将op数组与level数组还原为默认值*/OpStrackArray.level[OpStrackArray.top-1]=0;OpStrackArray.top--;/*top索引-1*/return c;/*返回栈顶操作符*/}/*将操作数压入栈*/void pushOd(float f){OdStrackArray.od[OdStrackArray.top]=f;/*操作数进入数组*/OdStrackArray.top++;/*top索引+1*/}/*操作符出栈,返回栈顶操作数*/float popOd(){float f=OdStrackArray.od[OdStrackArray.top-1];/*取得栈顶操作数*/OdStrackArray.od[OdStrackArray.top-1]=0.0;/*将od数组还原为默认值*/ OdStrackArray.top--;/*top索引-1*/return f;/*返回栈顶操作数*/}/*根据传入操作符运算,并将结果压入操作数栈*/ void compute(char op){float tempresult;/*临时计算结果*/float od1=popOd();float od2=popOd();/*连续pop出两个操作数*/switch(op)/*根据传入操作符计算*/{case '+':tempresult=od2+od1;break;case '-':tempresult=od2-od1;break;case '*':tempresult=od2*od1;break;case '/':tempresult=od2/od1;break;}pushOd(tempresult);/*将计算结果压栈*/}以下为一边扫描一边计算算法代码:#include "stdio.h"#include "stdlib.h"/*定义存放操作符的结构体*/struct OpStrack{char op[100];/*存放操作符的数组*/int top;/*栈顶索引*/int level[100];/*存放操作符优先级的数组*/}OpStrackArray;/*定义存放语素的结构体*/struct OdStrack{float od[100];/*存放操作数的数组*/int top;/*栈顶索引*/}OdStrackArray;/*声明需要用到的函数*/void pushOp(char,int);char popOp();void pushOd(float);float popOd();int stackIsEmpty();void judge(char,int);void compute(char);/*主函数*/void main(){char data[100];/*声明存放中缀表达式的字符串*/int i=0;/*作为遍历字符串的索引*/int eq=0;/*判断括号是否正确的条件*/float result;/*声明存放最终结果的float*/scanf("%s",data);/*输入中缀表达式*//*扫描输入的表达式并作出相应处理*/while(data[i]!='\0'){char temp[20]={'\0'};/*声明临时的存放操作数的数组并附初值*/int j=0;/*作为temp数组的索引*/float f;/*存放转换为float的数*/while((data[i]>='0'&&data[i]<='9')||data[i]=='.')/*如果是语素则存放至temp数组*/{temp[j]=data[i];j++;i++;}if(temp[0]!='\0')/*判断temp数组是否为空,是则将其转换为float并压栈*/{f=atof(temp);/*字符串转float*/pushOd(f);/*操作数入栈*/}switch(data[i]){case '+':judge(data[i],1);break;case '-':judge(data[i],1);/*加减优先级为1*/break;case '*':judge(data[i],2);break;case '/':judge(data[i],2);/*乘除优先级为2*/break;case '(':pushOp(data[i],-1);/*'('直接入栈,但入栈后优先级最小*/eq++;/*有一个'('eq+1*/break;case ')':while(OpStrackArray.op[OpStrackArray.top-1]!='(')/*')'不入栈*/{compute(popOp());/*不断弹出操作符并计算,直到遇到'('*//*如果发现还没碰到与之匹配的'('时栈已空则说明表达式不合法*/ if(stackIsEmpty()==1)/{printf("Expression is not valid!Press any key to continue...");getch();return;}}popOp();/*弹出'('*/eq--;/*如有与'('配队的')'则eq-1*/break;}i++;}/*扫描操作符栈内残余操作符并做相应计算*/while(stackIsEmpty()==0){compute(popOp());/*如果操作符栈不空则弹出操作符并计算*/}/*从操作数栈内弹出最终结果并输出*/if(eq==0)/*如果eq=0说明'('与')'可以完全配队,输入的表达式合法*/{result=popOd();printf("The result is:%.2f",result);/*结果取两位小数*/}else/*如果eq!=0说明'('与')'不能完全配队,输入的表达式不合法*/{printf("Expression is not valid!Press any key to continue...");}printf("\n");printf("Press any key to continue...");getch();}/*将操作符压入栈*/void pushOp(char op,int level){OpStrackArray.op[OpStrackArray.top]=op;/*操作符进入数组*/OpStrackArray.level[OpStrackArray.top]=level;/*为对应操作符附优先级*/OpStrackArray.top++;/*top索引+1*/}/*操作符出栈,返回栈顶操作符*/char popOp(){char c=OpStrackArray.op[OpStrackArray.top-1];/*取得栈顶操作符*/OpStrackArray.op[OpStrackArray.top-1]='\0';OpStrackArray.level[OpStrackArray.top-1]=0;/*分别将op数组与level数组还原为默认值*/ OpStrackArray.top--;/*top索引-1*/return c;/*返回栈顶操作符*/}/*将操作数压入栈*/void pushOd(float f){OdStrackArray.od[OdStrackArray.top]=f;/*操作数进入数组*/OdStrackArray.top++;/*top索引+1*/}/*操作符出栈,返回栈顶操作数*/float popOd(){float f=OdStrackArray.od[OdStrackArray.top-1];/*取得栈顶操作数*/OdStrackArray.od[OdStrackArray.top-1]=0.0;/*将od数组还原为默认值*/OdStrackArray.top--;/*top索引-1*/return f;/*返回栈顶操作数*/}/*判断操作符栈是否为空,是则返回1,否返回0*/int stackIsEmpty(){if(OpStrackArray.op[0]=='\0'){return 1;}return 0;}/*判断操作符优先级作出相应处理*/void judge(char op,int level){if(stackIsEmpty()==1||OpStrackArray.level[OpStrackArray.top-1]<level){/*如果栈为空或栈顶操作符优先级小于当前操作符优先级则将其压栈*/pushOp(op,level);}else{while(stackIsEmpty()!=1&&OpStrackArray.level[OpStrackArray.top-1]>=level){/*操作符不断出栈并计算直到栈为空或栈顶操作符优先级大于等于当前操作符优先级*/ compute(popOp());}pushOp(op,level);/*将当前操作符压栈*/}}/*根据传入操作符运算,并将结果压入操作数栈*/void compute(char op){float tempresult;/*临时计算结果*/float od1=popOd();float od2=popOd();/*连续pop出两个操作数*/switch(op)/*根据传入操作符计算*/{case '+':tempresult=od2+od1;break;case '-':tempresult=od2-od1;break;case '*':tempresult=od2*od1;break;case '/':tempresult=od2/od1;break;}pushOd(tempresult);/*将计算结果压栈*/}四、运行结果图3中缀转后缀算法运算结果图4 一边扫描一边计算算法运算结果五、遇到的问题及解决这部分我主要遇到了如下几个问题,其内容与解决方法如下所列:中缀转后缀算法:●问题1:扫描完成后输出的后缀表达式会带有’(’,从而导致计算不正确。