1基础-7逆波兰表示法(后缀表达式)

#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、最后看四则运算,加减号会把符号栈顶的加减乘除压入后缀栈,这就是说押入符号栈时栈顶只能是左弧或者空,而乘除号会把符号栈顶的乘除压入后缀栈,就是说乘除插入符号栈时栈顶只能是左弧或者加减,不难理解,加减在乘除下面则必然导致乘除先出栈再到加减出栈,也就是乘除先于加减运算

从整体角度看,分了加减,乘除,括号三种优先级的符号来运算
加减优先级最低,所以符号栈里的待入后缀栈符号,包括同级的加减(从左往右计算所以同级的先入栈的加减也是优先于当前加减)和乘除都可以马上入后缀栈,因为比当前符号优先运算
乘除次之,符号栈栈顶的同级的乘除先入栈(从左往右计算原则)
括号优先级最高(范围最高),一出场就要保证里面的要先算完再算括号外面,所以括号包着的都可以入后缀栈

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值