#include<stdio.h>
#include<string.h>
char sfex[1000]; //存储后序表达式,虽然没用到栈的性质,但下面的注释还是用后缀栈来描述
void transform(){ //函数功能:将中序表达式转化为后序表达式
char str[1000],stack[1000],ch; //表达式字符串、运算符栈、当前字符
int sum,i=1,t=1,top=0; //sum字符串长*、t数字栈栈顶指针、top是数字栈栈顶指针
scanf("%s",str+1); //输入中序表达式,注意下标从1开始
sum=strlen(str); //读出串长,即字符个数
str[sum++]='#'; //最后一个字符记为#
for(ch=str[i++];ch!='#';ch=str[i++]){//当前的字符不是#就表示还有数
if(ch=='(') //判定为左括号
stack[++top]=ch; //升栈存字符
else if(ch==')'){ //判定为右括号
while(stack[top]!='(') //符号栈退至左弧
sfex[t++]=stack[top--]; //符号栈的栈顶元素压入后缀栈中
top--; //符号栈中的左弧也要退掉
}else if(ch=='+'||ch=='-'){ //判定为加减号
while(top != 0 && stack[top] != '(')//若栈顶是+-*/
sfex[t++]=stack[top--];//把符号栈的栈顶元素压入后缀栈中
stack[++top]=ch; //符号压入符号栈
}else if(ch=='*'||ch=='/'){ //判定为乘除号
while(stack[top]=='*'||stack[top]=='/')若栈顶是*/
sfex[t++]=stack[top--];//把符号栈的栈顶元素压入后缀栈中
stack[++top]=ch; //符号压入符号栈
}
else{ //默认直接把数字压入数字栈
while(ch>='0'&&ch<='9') { //判定为数字
sfex[t++]=ch; //把数字压入数字栈
ch=str[i++]; //从字符串中取一个字符,下标后移
}
i--; //为了统一,因为判断跳出后有i++移至下一字符
sfex[t++] = '$'; //后缀栈中每个数字后加分隔符
}
}
while(top!=0)sfex[t++]=stack[top--];//符号栈仍有元素就把符号栈剩下的元素压进数字栈
sfex[t] = '#'; //后序表达式的最后加一个#作为结束符方便计算
for(int x=1;x<t;x++)printf("%c",sfex[x]);//输出后缀栈,注意这里上一句加的#没有输出
}
void calculate() { //计算后缀表达式的值
float stack[1000],d; //作为栈使用
int t=1,top=0; //t为ex下标,top为stack下标
char ch=sfex[t++]; //读出
while(ch!='#'){ //从左到右检查字符串,滤去#
if(ch=='+'){ //发现+运算符,用栈顶的两个元素进行+运算
stack[top-1]=stack[top-1]+stack[top],top--;//合并数字并退栈
}else if(ch=='-'){//发现-运算符,用栈顶的两个元素进行-运算
stack[top-1]=stack[top-1]-stack[top],top--;//合并数字并退栈
}else if(ch=='*'){//发现*运算符,用栈顶的两个元素进行*运算
stack[top-1]=stack[top-1]*stack[top],top--;//合并数字并退栈
}else if(ch=='/'){//发现/运算符,用栈顶的两个元素进行/运算
if (stack[top] != 0) //除数非0
stack[top - 1] = stack[top - 1] / stack[top]; //合并数字
else printf("\n\t除零错误!\n"); //报错
top--;//退栈
}else{
d=0; //累加数字初始为0
while(ch >='0'&&ch<='9'){ //当前位仍是数字
d=10*d+ch-'0'; //将数字字符转化为对应的数值
ch=sfex[t++]; //读出当前位置的数字,下标后移
}
stack[++top]=d; //数字压入栈
}
ch=sfex[t++]; //读出当前字符,而且刚好跳过了数字的#
}
printf("\n%g\n",stack[top]); //输出(数字)栈顶值就是结果
}
int main(){
transform(); //转化成逆波兰式
calculate(); //计算值
return 0;
}
/***
in 2*3+4*(11+22)/3+1
out 2$3$*4$11$22$+*3$/+1$+
51
***/
证明:
从操作角度看
解后缀时,开临时数字栈,每读到一个字符就合并栈顶两个元素
生后缀时,开后缀栈sfex即suffix expression与临时符号栈
1、先不看括号,易发现数字与符号是交错的,算法中数字直接入后缀栈,加减乘除四个符号都是倒序(因为数字正序押入)押完前面若干个符号入后缀后,自己再入符号栈,这就保证了他的运算分隔了左右的数,一定是对左右的部分进行运算;
2、在解后缀表达式时由左到右扫先出现的符号先运算,所以符号押入后缀栈的先后就是运算的先后,且易知每一时刻符号栈中由顶到底的符号优先级是依次降低的;
3、考虑括号,左括号时是不执行操作的,但出现右括号后就把两括号间的符号依次打进后缀栈,这保证了括号进面的运算形成一个整体,在解后缀时必然括号内的表达式优先会形成一个数
4、最后看四则运算,加减号会把符号栈顶的加减乘除压入后缀栈,这就是说押入符号栈时栈顶只能是左弧或者空,而乘除号会把符号栈顶的乘除压入后缀栈,就是说乘除插入符号栈时栈顶只能是左弧或者加减,不难理解,加减在乘除下面则必然导致乘除先出栈再到加减出栈,也就是乘除先于加减运算
从整体角度看,分了加减,乘除,括号三种优先级的符号来运算
加减优先级最低,所以符号栈里的待入后缀栈符号,包括同级的加减(从左往右计算所以同级的先入栈的加减也是优先于当前加减)和乘除都可以马上入后缀栈,因为比当前符号优先运算
乘除次之,符号栈栈顶的同级的乘除先入栈(从左往右计算原则)
括号优先级最高(范围最高),一出场就要保证里面的要先算完再算括号外面,所以括号包着的都可以入后缀栈