1、程序设计实训报告表达式求值问题完成者:何炜班级:计科1501学号:完成日期:2016年7月14日星期四一、题目的内容及要求求解形如(a+b)*(c+d)*e+f*h*g)的简单算术表达式的求值问题。这种表达式只包括加、减、乘、除4种运算符。为了实现表达式求值,可以首先读入原表达式(包括括号)并创建对应二叉树,其次对二叉树进行前序遍历、中序遍历、后续遍历(非递归),并输出逆波兰表达式,最后求解原表达式的值,同时对非法表达式格式能予以判断。用二叉树的结构来存储表达式,后续遍历二叉树即可得到逆波兰表达式二、需求分析本程序能解决形如(a+b)*(c+d)*e+f*h*g)并以#作为结束标志的简单算术表
2、达式的求值问题。不仅能够求解出多位浮点数,而且能够对简单的非法表达式进行判断以避免程序异常退出。三、概要设计1.用户输入中缀表达式2.程序将中缀表达式用二叉树的链式存储结构存储下来3.前序、中序遍历这颗二叉树,输出对应的前缀、中缀表达式4.后续遍历(非递归)这颗二叉树,并把遍历结果存储在顺序栈内,并输出后缀表达式5.对后缀表达式进行求值四、详细设计以下对概要设计进行详细的原理分析。1、输入中缀表达式,并存在字符型数组里定义字符串char strMAXSIZE;通过while(scanf(%s,str)!=EOF)来实现用户自定义结束程序的功能并通过strcmp(leave,str)=0 语句实
3、现当用户输入“leave”,程序会正常退出。2、以中缀表达式建立一棵二叉树定义二叉树的结构typedef struct BTNode ElemType datalength; struct BTNode *lchild; struct BTNode *rchild; BTNode;其中datalength;存储中缀表达式中的操作数或操作符。整体原理:i.已知输入的str1=(a+b)*(c+d)*e+f*h*g);定义BTNode *stMAXSIZE节点栈,用来存储树的节点。ii.再定义一个操作符栈char btMAXSIZE存储操作符,去扫描字符串的每个位置,遇到数字就不做处理直接放到节点
4、栈中,遇到操作符则将其与操作符栈顶元素比较优先级,决定是否送到节点栈中,以及决定该节点与其他节点的连接关系。iii.最后BTNode* &b记录下树的根节点,便得到一颗二叉树了。其中第一、第二步无需过多解释,以下详细解释第二步:1首先通过循环while(bttop1!=#|*p!=#)来扫描字符串的每个位置的元素2如果字符串该位置为数字,就直接建立树的节点,并把节点放进节点栈中。这里会遇到两种情况:一种是多位整数,一种是小数。多位整形数:遇到第一位数字时,新建树的节点,存到data数组里,用while(In(*p)=0)判断,将后面的几位数字也一起存到这个节点的data数组里,作为一个数字串整
5、体。带小数点的数:遇到第一位数字时,新建树的节点,存到data数组里,如果下一位是 . (小数点),则同样用while(In(*p)=0)判断,把小数点后面的数字连同前面的一起存到data数组里,作为一个数字串整体。代码如下:if(In(*p)=0)/字符串该位置是操作数,则直接建立二叉树节点 s=(BTNode*)malloc(sizeof(BTNode); s-lchild=NULL; s-rchild=NULL; s-datai=*p; p+; i+; while(In(*p)=0) s-datai=*p; p+; i+; if(*p=.)/如果是a.bc形式,则将a.bc一直存在节点中
6、,直至后面不再是操作数为止 s-datai=*p; i+; p+; while(In(*p)=0) s-datai=*p; p+; i+; s-datai=0; top2+; sttop2=s;/把该节点放入节点栈内 在此之前有一个判断是操作符还是操作数的判断函数,代码如下:int In(char c)if(c=+|c=-|c=*|c=/|c=(|c=)|c=#) return 1;/操作符else return 0;/操作数3如果是操作符,则比较该操作符与操作符栈顶元素的优先级各符号的优先级表如下:ab+-*/()#+-*/(g# 操作符栈顶操作符优先级,则让操作符入操作符栈,然后检查字符串
7、下一个位置b)如果当前操作符优先级 操作符栈顶操作符优先级,以操作符栈顶元素建立新的树节点并且取出节点栈顶的两个节点作为该节点的左右孩子节点,并把该节点压入节点栈。c)如果当前操作符优先级 = 操作符栈顶操作符优先级,此时,栈顶和当前操作符为左右括号,此时,出操作符栈顶元素,检查字符串中的下一个元素d)如果返回得到g,即反映了表达式错误,令全局变量error=999,以便在主函数中控制不输出结果。3、前序、中序遍历这颗二叉树,并输出前缀、中缀表达式这两种遍历采用递归方式。这种方式很简单。每次访问节点的时候,把节点存在相应表达式数组里,以便输出。4、后序遍历(非递归)二叉树,并把遍历结果存在字符
8、型指针数组里,并输出后缀表达式非递归后续遍历的思想:采用一个顺序栈才存储需要返回的节点BTNode *StMAXSIZE;,先扫描根节点所有左孩子节点,并把之前经过的节点一一进栈,到了最后,出栈一个节点,即为父亲节点,再扫描该节点的右孩子节点的左孩子节点,并把之前经过的节点一一进栈。当一个节点的左右孩子节点均访问后再访问该父亲节点,直至栈空为止。当访问该节点的时候,就把该节点数值存放到字符型指针数组里,并输出该值。5、通过后缀表达式求出表达式的值因为本程序能解决简单的二目运算符的运算,所以定义double opnd1,opnd2作为运算符的两个元素,定义double opnd 作为运算结果。定
9、义一个数栈double StMAXSIZE来存放后缀表达式结果。依次扫描后缀表达式(即postexpn数组)i.如果是数字,同上面建立二叉树一样,分为两种:多位整数和小数,我们通过循环for(j=0;jr;j+)(其中r为数字串的长度),只要发现有小数点,就让flag=1;以此分辨两种情况,并定义k,统计小数点后面的位数,以便在还原小数的值的时候乘以相应的数量级即可!还原数字串原本的数值,再讲数值压入到数栈内。ii.如果是符号,就把数栈内的两个数字取出来(top-)赋值给opnd1, opnd2.计算出结果,再压入数栈。通过while (in)控制循环条件,最后St0即为表达式的结果。五、源代
10、码#include #include #include #include /*定义区*/#define MAXSIZE 256 #define length 200#define ElemType charint error=0; /二叉树的链式存储结构 typedef struct BTNode ElemType datalength; struct BTNode *lchild; struct BTNode *rchild; BTNode;/判断是操作符还是操作数 int In(char c)if(c=+|c=-|c=*|c=/|c=(|c=)|c=#) return 1;/操作符else
11、 return 0;/操作数 /判断操作符的优先级 char Precede(char a,char b) char r; switch(a) case +: case -:if(b=*|b=/|b=() r=;break; /加减比乘除和左括号的优先级低 case *: case/:if(b=()r=;break;/乘除比左括号的优先级低 case(:if(b=)/左右括号的优先级相同 r=; else if(b=#) printf(表达式错误n); r=g; else r=;/右括号比其他符号优先级高 break; case#:if(b=) printf(表达式错误n); r=g; els
12、e if(b=#)r=; else r=lchild=NULL; s-rchild=NULL; s-datai=*p; p+; i+; while(In(*p)=0) s-datai=*p; p+; i+; if(*p=.)/如果是a.bc形式,则将a.bc一直存在节点中,直至后面不再是操作数为止 s-datai=*p; i+; p+; while(In(*p)=0)/当下一个位置是操作符,就存储到节点数组中 s-datai=*p; p+; i+; s-datai=0; top2+; sttop2=s;/把该节点放入节点栈内 else /字符串该位置是操作符,比较该操作符和操作符栈顶元素的优先
13、级 switch(Precede(bttop1,*p) case 栈顶操作符优先级,则让操作符入操作符栈 case : s=(BTNode*)malloc(sizeof(BTNode); s-datai=bttop1; i+; s-datai=0; top1-; s-rchild=sttop2; top2-; s-lchild=sttop2; sttop2=s; break; /如果当前操作符优先级 data; printf(%s ,b-data); n1+; PreOrder(b-lchild); PreOrder(b-rchild); /中序遍历得到前缀表达式char *inexpMAXS
14、IZE;int n2=0;void InOrder(BTNode *b) if(b!=NULL) InOrder(b-lchild); inexpn2=b-data; printf(%s ,b-data); n1+; InOrder(b-rchild); /对二叉树进行后序遍历得到后缀表达书 /后序遍历 char* postexpMAXSIZE;/存放后缀表达式,每个节点都是一个字符型数组,故这里使用字符型指针数组int n3=0;void PostOrder(BTNode *b) BTNode *StMAXSIZE;/保存需要返回节点指针 BTNode *p; int flag,top=-1
15、; if(b!=NULL) do while(b!=NULL) top+; Sttop=b; b=b-lchild; p=NULL; flag=1; while(top!=-1 & flag) b=Sttop; if(b-rchild=p) postexpn3=b-data; printf(%s ,b-data); n3+; top-; p=b; else b=b-rchild; flag=0; while(top!=-1); printf(n); /后缀表达式求值 double CompValue() int r;/字符串的长度 double StMAXSIZE;/数栈 double opn
16、d,opnd1,opnd2;/opnd1,opnd2分别为数栈栈顶的前两个元素,opnd为计算结果 char chlength; int top=-1; int i=0; int j; double sum;/不能写成int sum,否则当小数点位数过多,会出现错误! while (in3) strcpy(ch,postexpi); i+; if(strcmp(ch,+)=0) opnd1=Sttop;top-; opnd2=Sttop;top-; opnd=opnd1+opnd2; top+; Sttop=opnd; else if(strcmp(ch,-)=0) opnd1=Sttop;t
17、op-; opnd2=Sttop;top-; opnd=opnd2-opnd1; top+; Sttop=opnd; else if(strcmp(ch,*)=0) opnd1=Sttop;top-; opnd2=Sttop;top-; opnd=opnd2*opnd1; top+; Sttop=opnd; else if(strcmp(ch,/)=0) opnd1=Sttop;top-; opnd2=Sttop;top-; if(opnd1=0) printf(不能除以0n); error=999;/error为错误变量,以便最后不输出任何结果 return 0; opnd=opnd2/op
18、nd1; top+; Sttop=opnd; else int k; int flag=0;/判断是小数还是多位整数、单位整数三种情况 sum =0; r=strlen(ch); for(j=0;jr;j+) if(chj=.) k=-1; flag=1; else sum=sum*10+(chj-0); k+; top+; if(flag=0) Sttop=sum; else Sttop=sum*pow(10,-k); / printf(sum=%); / printf(还原=%fn,Sttop); / printf(%fn,Sttop);/这个地方如果用%d,就没办法输出浮点数! retu
19、rn St0; double ExpValuel(BTNode *b) printf(后缀表达式:); PostOrder(b); return (CompValue(); int main () BTNode *B;/根节点的指针 double re;/结果变量 char strMAXSIZE;/中缀表达式字符串 printf(*n); printf( 这里是计算器程序,包含以下功能n); printf( 1.支持二目运算符的多位整数、单个整数、小数混合的加减乘除n); printf( 2.支持对一些简单的非法表达式的判断n); printf( 3.你可以在任何时候输入leave 退出程序n
20、); printf( 4.所输入必须是英文符号,以#号键结束!n); printf(*n); printf( 现在开始!n); while(scanf(%s,str)!=EOF) if(strcmp(leave,str)=0) printf(程序正确退出!n); break; ExpBTree(B,str);/建立二叉树 if(error!=999) printf(前缀表达式:); PreOrder(B); printf(n); printf(中缀表达式:); InOrder(B); printf(n); re=ExpValuel(B);/后序遍历并且计算结果 printf(运算结果:%.2f
21、n,re);/输出结果保留两位小数 n1=0; n2=0; n3=0; error=0; return 1;六、运行结果及分析结果分析:输入的括号必须为英文括号,而且表达式需要以#结束,否则无法输出正确结果。该表达式求值程序能够求多位整数、小数单独或者混合加减运算,不仅能够得到正确结果,而且能够输出前缀、中缀、后缀表达式。该程序能够对简单的非法表达式进行进行判断,并且输出表达式错误信息而且不输出任何数值。但是对复杂的非法表达式不能进行主动判断,只能异常退出。该程序能够实现主动退出,输入“leave”就会正确退出。七、收获及体会1、没有完美的程序,一个程序是需要不断修改,不断找出问题并更改的。这
22、个程序一开始只能算个位数的加减乘除,因为一开始,中缀表达式是以字符串的形式存到内存里的,我们需要对字符串的每个字符进行扫描,如何判断一串连续数字是一个整体,这是一个问题,如何判断这一连串数字时小数,也是一个问题。但是经过思考,其实发现,这个也并不会很难。只需要对下一个字符进行判断即可!后面修订的的程序对非法表达式也不能判断,一旦输入出错,会直接异常退出,而不会给用户提示信息。所以,在后面,我又加了几项,以便能够对简单的非法表达式进行判断。但,事实上,限于能力,还有一部分非法表达式,这个程序是无法判断,只能异常结束。一个小小程序完成,我发现尽管可能基本满足需要,但是需要改进的地方总是无穷无尽的,要想一个程序达到尽善尽美,是需要很大耐心、毅力和能力的。2、关注细节错误写程序最难的是找错误!
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1