利用栈实现算术表达式的求值
来源:互联网 发布:金马甲网络交易平台 编辑:程序博客网 时间:2024/05/17 03:39
以下源码均为原创,仅供参考,如有差错或优化欢迎指正交流。
以下出现源码均由DevC++编译通过。
下面是原题目
利用栈实现算术表达式的求值
[问题描述]
利用栈实现算术表达式的求值。可以简单一些,假设表达式中含有一位正整数,以及+、-、*、/、(、)。但不受此限制。(难易程度:中)
[实验内容及要求]
1、表达式以字符串形式输入,并以‘#’开始和结束(‘#’也作为算法来处理)。如输入:#6+3*(9-7)-8/2#
2、能够有效判别表达式的输入格式是否有误(如缺失操作数、非法算符、括号不匹配等错误),若输入格式错误,输出错误提示。
[测试数据]
1、#6+3*(9-7)-8/2#
2、#(8-2)/(3-1)*(9-6)#
3、#5+a*(8-4)/2#
4、#5+(7-3)*6#
[题目简析]
本题中给出了描述,运算数均为“一位正整数”,这个条件大大降低了本题的难度,可以将数字和字符一同输入,然后再进行分开处理。[基本步骤]
- 中缀表达式——>后缀表达式(逆波兰表达式)(利用栈)
- 后缀表达式求解(利用栈)
[程序细节]
- 表达式通过字符串的方式输入和存储
- 使用的栈类型为顺序栈
- 数字以字符型变量存储与整型变量存储的区别和相互转化
[实现过程]
先给出需要用到的功能性子函数。
这些函数均是基础性内容,不是本例重点。
本例使用的是顺序栈,以下给出顺序栈的结构类型定义和基本操作的子函数定义。
顺序栈结构类型定义
typedef struct{char data[50];int top;}stack;//顺序栈类型定义
void newstack(stack *&S)//初始化顺序栈{S=(stack*)malloc(sizeof(stack));S->top=-1;} int push(stack*S,char e)//进栈 {if(S->top>49){printf("in error!\n");return 0;}else{S->data[++S->top]=e;return 1;}} int empty(stack*S)//判断栈空{if(S->top<0) return 1;else return 0; } char pop(stack*S)//出栈{char e;if(empty(S)){printf("out error!\n");return 0;}else{e=S->data[S->top--];return e;}}
表达式字符串中数字和运算符号均以字符型变量存储,需要对数字进行识别,以及整型与字符型的转变。以下给出相关功能的函数。
字符型变量是否是数字
int isnum(char e)//判断是否为数字 {if(e>='0'&&e<='9') return 1;else return 0;}
字符型转化为整型
int num(char e)//字符型转换为整型 {int n;n=e-48;//ASCII码 return n;}
整型转化为字符型
char nonum(int n)//整型转换为字符型 {char e;e=n+48;//ASCII码 return e;}
下面依据题目要求进行主体内容的编码实现。
题目中要求需要对输入的表达式正确性进行必要的判断,其中括号是否匹配的判断单独进行更为简便。以下给出该功能的函数。
int correct(char s[])//判断括号是否匹配 {stack *S;newstack(S);int flag=1,i=1;while(s[i]!='#'&&flag){if(s[i]=='(') push(S,s[i]);if(s[i]==')') if(pop(S)!='(') flag=0;i++;}//最先遇到的后括号前必定是与之对应的前括号,如若匹配不成功,则flag记为0,即括号不匹配if(!empty(S)) flag=0;return flag;}
本例的第一个重点,也是第一个重要的考察点,即中缀表达式转化为后缀表达式。(这里引用后缀表达式的介绍,这是本程序实现的必要途径。)
转化过程中的要点
- 运算符号优先级的判断
- 运算符号遇到括号时的优先级判断
括号的问题将单独考虑,所以在对运算符优先级的判断中暂不考虑这个问题,只对加‘+’、减‘-’、乘‘*’、除‘/’进行判断,以下给出该功能函数。
int rank(char a,char b)//判断运算符优先级,不包括括号的优先级 {if((a=='*'||a=='/')&&(b=='+'||b=='-')) return 1; else return 0;}
之后是中缀表达式转化为后缀表达式的实现,思路描述如下:
中缀表达式a + b*c + (d * e + f) * g,其转换成后缀表达式则为a b c * + d e * f + g * +。
转换过程需要用到栈,具体过程如下:
1)如果遇到操作数,我们就直接将其输出。
2)如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。
3)如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。
4)如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) "的情况下我们才弹出" ( ",其他情况我们都不会弹出" ( "。
5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
对该过程仍有疑问的可以阅读以下推荐链接,了解该过程的转化算法思路。
链接1:后缀表达式——凌波ling
链接2:中缀表达式转化为后缀表达式——石锅拌饭
通过以上描述,给出实现以上功能的源码。
void trans(char s1[],char s2[])//中缀表达式转换为后缀表达式,s1为中缀表达式,s2为后缀表达式{int i=1,j=0;char e;stack *S;newstack(S);while(s1[i]!='#'){if(isnum(s1[i]))//是数字直接输出 s2[j++]=s1[i];else//运算符号,括号的处理 {if(s1[i]=='(')//前括号直接入栈 push(S,s1[i]);if(s1[i]==')')//遇到后括号输出栈顶运算符直至遇到左括号,左括号出栈但不输出 while((e=pop(S))!='(')s2[j++]=e;if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/')//栈空入栈,栈顶为左括号入栈,优先级高于栈顶运算符入栈,否则出栈并再次判断,实际实现时先进行是否出栈判断 {while(!(empty(S)||S->data[S->top]=='('||rank(s1[i],S->data[S->top]))) s2[j++]=pop(S); if((empty(S)||S->data[S->top]=='('||rank(s1[i],S->data[S->top]))) push(S,s1[i]);} if(!(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/'||s1[i]=='('||s1[i]==')')) printf("非法运算符!\n"); //除过四则运算和括号(+、-、*、/、(、))外,其余判定为非法运算符}i++;}while(!empty(S))//表达式读毕将栈内运算符一一输出 s2[j++]=pop(S);s2[j]='\0';//字符串结尾 }
最后进行后缀表达式的求值,如果理解后缀表达式的话,这个步骤应用栈很容易实现,这里不做赘述。需要注意的是,是否缺少运算符的判断将在这一步最后进行。
下面给出这部分子函数。
int workout(char s2[])//计算后缀表达式的值 {int a,b,i=0;stack *S;newstack(S);while(s2[i]!='\0'){if(isnum(s2[i]))//数字入栈等待运算符操作 push(S,s2[i++]);else{b=num(pop(S)); a=num(pop(S));//字符型转为整型进行运算 switch(s2[i++]) { case'+': a=a+b; push(S,nonum(a)); break; case'-': a=a-b; push(S,nonum(a)); break; case'*': a=a*b; push(S,nonum(a)); break; case'/': a=a/b; push(S,nonum(a)); break;//运算后转为字符型入栈 default: printf("error!");}}}if(S->top!=0)//缺少运算符时,输出提示,但仍然带回栈顶元素的值作为运算结果输出{printf("缺少运算符!得到中间结果!\n");}a=num(pop(S));//字符型转为整形带入返回值 return a;}
至此整个题目中需要的功能的子函数都已经完成,只需要在主函数中调用实现相应的功能就可以。对括号的判断过程和结果输出包括在主函数中。
下面给出主函数的源码。
int main(){int ans;char s1[80],s2[80];printf("输入表达式:"); gets(s1);if(!(correct(s1))){printf("括号不匹配!");return 0;} else{ trans(s1,s2); printf("后缀表达式:"); puts(s2); ans=workout(s2); printf("ans=%d\n",ans); system("pause"); return 0;}}
整个程序的主体已经完成,将上文的源码进行编译得到符合题目要求的可执行文件。
测试数据的测试
- #6+3*(9-7)-8/2#
- #(8-2)/(3-1)*(9-6)#
- #5+a*(8-4)/2#
- #5+(7-3)*6#
#6+3*(9-7)-8/2#
#(8-2)/(3-1)*(9-6)#
#5+a*(8-4)/2#
第三个表达式中有一个运算数为字母'a',在中缀转后缀处理过程中将作为运算符进行判断,经过判断为“非法运算符”,所以不入栈也不输出在后缀表达式中。
在计算后缀表达式的过程中,因为字符串仍有运算符存在,但栈中存储的数的数量因为缺少了'a'的位置,所以实际上不够两个,出栈时就会出错,出现出栈失败时会出现的“out error!”字样。
所以最后输出的结果其实也是一个没有壹壹的运算结果。
#5+(7-3)*6#
在目前的试验数据中尚未发现任何bug。
如果发现有源码中不完善的地方,欢迎交流讨论。
- 利用栈实现算术表达式的求值
- 利用栈实现简单算术表达式求值
- 利用栈实现算术表达式求值(Java语言描述)
- 利用栈实现算术表达式求值(Java语言描述)
- 利用栈实现的后缀形式的算术表达式的求值的c++程序
- 栈的算术表达式求值
- 栈的操作和c语言实现算术表达式求值
- 算术表达式求值(顺序栈实现)
- 栈的应用--算术表达式求值
- 栈和队列-算术表达式的求值
- 栈的应用之算术表达式求值
- 双栈算术表达式求值
- 表达式求值-栈的利用
- 数据结构Java实现——①栈-->栈的应用三、算术表达式求值
- js实现计算器 提供算术表达式求值
- Dijkstra的双栈算术表达式的求值算法
- 通过算术表达式求值说明栈的应用
- Dijkstra的双栈算术表达式求值算法
- HADOOP源码编译
- AttributeError: 'module' object has no attribute '_base' 可行解决办法
- 查找之Hash查找
- 关于使用ant-design设计可动态添加FormItem的过程中遇到的问题记录。
- spark-shell启动报错:Yarn application has already ended! It might have been killed or unable to launch ap
- 利用栈实现算术表达式的求值
- Caffe中HDF5Data例子
- Springmvc静态资源配置
- hdu 6228 Tree
- 机房收费系统之结账
- 已知满二叉树的先序遍历,求其后序遍历
- matlab——深度学习NN
- centos6.5安装solr集群版
- Longest Ordered Subsequence POJ