1、题目设计一个程序实现基于二叉树表示的算术表达式的操作题目:设计一个程序实现基于二叉树表示的算术表达式的操作。一、 需求分析1、以二叉树为基本模型,构建了表达式二叉树。算术表达式的合法输入数据包括变量(,az)、常量(09)和二元运算符(,(乘幂),一元运算符(sin, cos,tan)。演示程序以人机对话的方式执行,即在计算机上显示提示信息后,由用户在键盘上输入对应的数据或命令,程序将执行相应的操作并显示下一步信息。表达式的输出主要是用带括号的中缀表示式输出调用函数InorderExp( ExpTree E, Status ( * Visit )( ExpTree e ) );2、 程序的目的
2、实现算术表达式在计算机里的树形存储,实现基本的运算(,(乘幂)sin,cos,tan),求偏导,常数合并。3、 测试数据( 附后 )。提供两种方式的测试:一种是自动测试,即程序调用test文件夹data.txt文件里的测试数据,另一种方式是手动测试,即按程序提示一步一步输入测试。除了满足要求的0; a; -91; +a*bc; +*5x2*8x; +*3x3*2x2x6,还有几十组数据测试。每当输入一个表达式后,程序提示用户赋值,再对表达式求值。为了方便用户,我在程序中用数组保存着一些测试数据,以供测试用。二、概要设计1以字符串保存输入的字符序列。2提示用户赋值的同时将数据取出建立二叉树。3用
3、后根遍历的次序用递归函数对表达式求值,求值时进行相应的转化,将运算数的字符形式转换成整数形式。4用中缀表达式输出表达式时,适当添加括号,以正确反映运算的优先次序。5抽象数据类型的定义:1)、存放表达式的结构类型,是以二叉树为基本原型。typedef enum OPER, VAR, ORD ElemTag;/运算符,变量,常量typedef struct ExpNode ElemTag tag; /标记 union char expr4; /存放运算符名 struct char var; /存放变量名 int val; /存放变量的值,初始值为0 vary; /存放变量 int ordina;
4、/存放常量值 ; struct ExpNode *lchild, *rchild; /* 左右孩子指针 */ *ExpTree; /* 二叉树的二叉链表存储表示 */基本操作:int Random( int nMin, int nMax );/返回nMin到nMax之间的随机数void FindVary( char * c, char * e );/找出表达式中的变量Status ArrayCreateExp( ExpTree &E, char *ch, int &i );/从ch数组中读取字符串,构造表达式void CreateExp( ExpTree &E, char *ch, int &
5、i ) ;/Status InputCreateExp( ExpTree &E ); /从键盘先序输入来构造表达式树TStatus Visit( ExpTree e );/输出e的内容void InorderExp( ExpTree E, Status ( * Visit )( ExpTree e ) );/输出中序表达式用带括号的中缀表示式输出Status Assign( ExpTree E, char v, float c ) ;/对表达式内的所有v,赋值cfloat Value( ExpTree E );/计算表达式的值ExpTree Compound( char p, ExpTree
6、e1, ExpTree e2 );/5.构造一个新的复合表达式(E1)P(E2) Status Diff( ExpTree &E, char V );/求表达式E对变量V的导数void MergeConst( ExpTree E );/合并表达式种所有常数运算Status PreOrderTraverse( ExpTree E, Status ( * Visit )( ExpTree e ) );/波兰式输出Status PostOrderTraverse( ExpTree E, Status ( * Visit )( ExpTree e ) );/逆波兰式输出2)、队列typedef cha
7、r QElemType; typedef struct QNode QElemType data; struct QNode *next;QNode, *QuePtr;typedef struct QuePtr front; QuePtr rear;Queue;基本操作:Status InitQueue( Queue &Q );/构造一个空队列Status DestroyQueue( Queue &Q );/销毁队列Status QueueEmpty( Queue Q );/判空Status EnQueue( Queue &Q, QElemType e );/插入元素e为Q的新的队尾元素Sta
8、tus DeQueue( Queue &Q, QElemType &e );/删除队头元素,用e返回其值,并返回OK,否则返回ERROR;3)、栈typedef struct SElemType *base; SElemType *top; int stacksize;SqStack;基本操作:Status InitStack( SqStack &S );Status StackEmpty( SqStack S );Status Push( SqStack &S, SElemType e );Status Pop( SqStack &S, SElemType &e );SElemType To
9、p( SqStack S );6、 主程序:void main()while(1) 接受命令 处理命令;Switch() case: 1.以数组形式输入前缀表示式函数构造表达式. case: 2.以字符序列输入前缀表示式函数构造表达式. case: 3.实现对变量V的赋值(V=c). case: 4.对算术表达式E求值.n); case: 5.构造一个新的复合表示式(E1)P(E2). case: 6.求偏导函数Diff(E,V) case: 7.对三角函数的测试. case: 8.常数合并. case: 0.结束 三、详细设计1、存放表达式的结构类型,是以二叉树为基本原型。typedef e
10、num OPER, VAR, ORD ElemTag;/运算符,变量,常量typedef struct ExpNode ElemTag tag; /标记 union char expr4; /存放运算符名 struct char var; /存放变量名 int val; /存放变量的值,初始值为0 vary; /存放变量 int ordina; /存放常量值 ; struct ExpNode *lchild, *rchild; /* 左右孩子指针 */ *ExpTree; /* 二叉树的二叉链表存储表示 */我原来是直接用二叉树的存储结构的,后来发现受到这个结构类型的很大限制,受到广义表存储结
11、构的启发,就自己设计了这样一个存储类型。下面分析这个存储结构:(1)、用ElemTag tag;来标记是运算符,变量,常量。用枚举类型定义typedef enum OPER, VAR, ORD ElemTag,可以区分是运算符,变量,常量。(2)、用字符串char expr4; 来存放运算符名,我先预定存放三个字符的运算符名(最后一个char用来存放0),这样运算符不仅可以是+ , - , *,/,还可以是sin,cos,tan。如果觉得三个字符不够,可以扩展。(3)、struct char var; /存放变量名 float val; /存放变量的值,初始为0 vary;/存放变量。这样在变
12、量赋值后,还可以保存着变量名。可以用作如公式一样,重复赋值使用。(4)、使用int ordina;来存放常量值。这样赋值时,就扩大了赋值范围,可以是一个整形的范围,大大扩大了本程序的使用范围。但需要在赋值时使用,在输入时,还是得用09,这也是本程序的缺陷,有待改进。基本操作:int Random( int nMin, int nMax );/返回nMin到nMax之间的随机数void FindVary( char * c, char * e );/找出表达式中的变量Status ArrayCreateExp( ExpTree &E, char *ch, int &i );/从ch数组中读取字符
13、串,构造表达式void CreateExp( ExpTree &E, char *ch, int &i ) ;/Status InputCreateExp( ExpTree &E ); /从键盘先序输入来构造表达式树TStatus Visit( ExpTree e );/输出e的内容void InorderExp( ExpTree E, Status ( * Visit )( ExpTree e ) );/输出中序表达式用带括号的中缀表示式输出Status Assign( ExpTree E, char v, float c ) ;/对表达式内的所有v,赋值cfloat Value( ExpT
14、ree E );/计算表达式的值ExpTree Compound( char p, ExpTree e1, ExpTree e2 );/5.构造一个新的复合表达式(E1)P(E2) Status Diff( ExpTree &E, char V );/求表达式E对变量V的导数void MergeConst( ExpTree E );/合并表达式种所有常数运算Status PreOrderTraverse( ExpTree E, Status ( * Visit )( ExpTree e ) );/波兰式输出Status PostOrderTraverse( ExpTree E, Status
15、( * Visit )( ExpTree e ) );/逆波兰式输出2、队列。typedef char QElemType; typedef struct QNode QElemType data; struct QNode *next;QNode, *QuePtr;typedef struct QuePtr front; QuePtr rear;Queue;基本操作:Status InitQueue( Queue &Q );/构造一个空队列Status DestroyQueue( Queue &Q );/销毁队列Status QueueEmpty( Queue Q );/判空Status E
16、nQueue( Queue &Q, QElemType e );/插入元素e为Q的新的队尾元素Status DeQueue( Queue &Q, QElemType &e );/删除队头元素,用e返回其值,并返回OK,否则返回ERROR;3、栈typedef struct SElemType *base; SElemType *top; int stacksize;SqStack;基本操作:Status InitStack( SqStack &S );Status StackEmpty( SqStack S );Status Push( SqStack &S, SElemType e );St
17、atus Pop( SqStack &S, SElemType &e );SElemType Top( SqStack S );4、主函数和其他主要函数1)、访问函数(输出函数)Status Visit( ExpTree e )/输出e的内容 int m, n5; if( e-tag = OPER ) /运算符 printf(%s, e-expr ); else if( e-tag = VAR ) /变量 if( !e-vary.val ) /变量的值是0 printf(%c, e-vary.var );/输出变量名 else printf(%0.4f, e-vary.val );/输出变量的
18、值 else /常量 if( e-ordina = 0 ) /正数 printf(%d, e-ordina ); else printf(%d), e-ordina ); /负数,输出时加括号 return OK;2)、构造表达式Status ArrayCreateExp( ExpTree &E, char *ch, int &i )/从ch数组中读取字符串,构造表达式 if( chi ) if( ! ( E = ( ExpTree )malloc( sizeof( ExpNode ) ) ) ) return ERROR; if( chi = s & chi+1 = i & chi+2 =
19、n |/sin chi = S & chi+1 = I & chi+2 = N | chi = c & chi+1 = o & chi+2 = s |/cos chi = C & chi+1 = O & chi+2 = S | chi = t & chi+1 = a & chi+2 = n |/tan chi = T & chi+1 = A & chi+2 = N ) E-tag = OPER; E-expr0 = chi; E-expr1 = ch+i; E-expr2 = ch+i; E-expr3 = 0; ArrayCreateExp( E-rchild, ch, +i );/只建右子
20、树 E-lchild = NULL; /左子树为空 return OK; else if( chi = 0 & chi tag = ORD; E-ordina = chi - 0; else if( chi = a & chi tag = VAR; E-vary.var = chi; E-vary.val = 0; else if( chi = + | chi = - | chi = * | chi = / | chi = )/运算符 E-tag = OPER; E-expr0 = chi; E-expr1 = 0; if( ! E-tag ) / E 是运算符 ArrayCreateExp(
21、 E-lchild, ch, +i ); ArrayCreateExp( E-rchild, ch, +i ); else E-lchild = NULL; E-rchild = NULL; return OK;3)、带括号的中缀表示式输出void InorderExp( ExpTree E, Status ( * Visit )( ExpTree e ) )/输出中序表达式用带括号的中缀表示式输出 int bracket; if( E ) if( E-lchild ) bracket = precede( E, E-lchild );/比较双亲与左孩子运算符优先级 if( bracket 0
22、 ) /左孩子优先级低 printf(); InorderExp( E-lchild, Visit ); if( bracket 0 ) printf(); Visit( E ); if( E-rchild ) bracket = precede( E, E-rchild );/比较双亲与右孩子运算符优先级 if( bracket = 0 ) /右孩子优先级低 printf(); InorderExp( E-rchild, Visit ); if( bracket = 0 ) printf(); 4)、计算表达式的值float Value( ExpTree E )/计算表达式的值 float
23、lv,rv,value = 0; if( E ) if( E-tag = VAR ) /是变量 return ( E-vary.val ); if( E-tag = ORD ) return ( E-ordina ); if( E-lchild ) lv = Value( E-lchild ); rv = Value( E-rchild ); switch( E-expr0 ) case +: value = lv + rv; break; case -: value = lv - rv; break; case *: value = lv * rv; break; case /: if( r
24、v ) value = lv / rv; else exit( 0 ); break; case : value = power( lv, rv ); break; case S: case s: value = sin( rv ); break;/sin case T: case t: value = tan( rv ); break;/tan case C: case c: if( E-expr2 = S | E-expr2 = s )/cos value = cos( rv ); break; return ( value );5)、合并常数void MergeConst( ExpTre
25、e E )/合并表达式中所有常数运算 if( ! E-lchild & ! E-rchild ) return; /叶子 if( E-tag = OPER & E-lchild & E-rchild & E-lchild-tag = ORD & E-rchild-tag = ORD ) E-tag = ORD; switch( E-expr0 ) case *: E-ordina = E-lchild-ordina * E-rchild-ordina; break; case /: E-ordina = E-lchild-ordina / E-rchild-ordina; break; cas
26、e : E-ordina = power( E-lchild-ordina, E-rchild-ordina ); break; case +: E-ordina = E-lchild-ordina + E-rchild-ordina; break; case -: E-ordina = E-lchild-ordina - E-rchild-ordina; break; free( E-lchild ); free( E-rchild ); E-lchild = NULL; E-rchild = NULL; else if( E-lchild ) MergeConst( E-lchild );
27、 if( E-rchild ) MergeConst( E-rchild ); 5、构造的表达式如图算术表达式前缀表示:*+ab-cd中缀带括号表示:(a+b)*(c-d)四、 调试分析1、调试过程中遇到了许多问题,下面举几个例子。(1)赋值出错处理在调试赋值函数时发生了“赋值失败的情况”修改前的函数:void FindVary( char *c, char * e )/找出表达式中的变量 int i = -1, j = 0; while( e+i ) if( ei = A & ei = a & ei = z ) i += 2; continue; else cj+ = ei; cj = 0;调试出现下面的情况出现了两次了对X的赋值,原因是我在编写void FindVary( char *c, ch
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1