BUAA数据结构第五次编程题——计算器(表达式树实现)
看前须知
第五次上机题汇总
实验:树的构造与遍历——根据提示循序渐进(可惜提示有问题Ծ‸Ծ).
题目内容
问题描述
从标准输入中读入一个整数算术运算表达式,如24 / ( 1 + 5%3 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 )= ,计算表达式结果,并输出。
要求:
-
表达式运算符只有+、-、*、/、%,表达式末尾的=字符表示表达式输入结束,表达式中可能会出现空格;
-
表达式中会出现圆括号,括号可能嵌套,不会出现错误的表达式;
-
出现除号/时,以整数相除进行运算,结果仍为整数,例如:5/3结果应为1。
-
要求采用表达式树来实现表达式计算。
表达式树(expression tree):
我们已经知道了在计算机中用后缀表达式和栈来计算中缀表达式的值。在计算机中还有一种方式是利用表达式树来计算表达式的值。表达式树是这样一种树,其根节点为操作符,非根节点为操作数,对其进行后序遍历将计算表达式的值。由后缀表达式生成表达式树的方法如下:
读入一个符号:
如果是操作数,则建立一个单节点树并将指向他的指针推入栈中;
如果是运算符,就从栈中弹出指向两棵树T1和T2的指针(T1先弹出)并形成一棵新树,树根为该运算符,它的左、右子树分别指向T2和T1,然后将新树的指针压入栈中。
例如输入的后缀表达为:
ab+cde+**
则生成的表达式树为:
居中的图片:
输入形式
从键盘输入一个以=结尾的整数算术运算表达式。操作符和操作数之间可以有空格分隔。
输出形式
首先在屏幕上输出表达式树根、左子节点及右子节点上的运算符或操作数,中间由一个空格分隔,最后有一个回车(如果无某节点,则该项不输出)。然后输出表达式计算结果。
样例
输入
24 / ( 1 + 2 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 ) =
输出
*/ /
18
样例说明
按照运算符及括号优先级依次计算表达式的值。在生成的表达树中,*是根节点的运算符,/ 是根节点的左子节点上运算符,/是根节点的右子节点上运算符,按题目要求要输出。
题解
易错点和难点
如果之前写过后缀表达式的表达式计算,这道题那就不怎么难了。本题就是维护两个栈,一个树栈,一个运算符栈,树栈的操作要求确实比较高,因为里面涉及到树节点的拷贝,删除等等,如果操作不小心,极有可能造成栈中信息的丢失(笔者一开始在树栈弹出两个元素之后对那两个元素的位置进行清空,然后不小心把弹出栈的两个元素也同时清零的,当时还困惑了很久,最后还是采用拷贝节点信息比较靠谱,使节点信息和出栈信息分开)。
本题难点的部分还在于怎么处理运算符的优先级,对于这个问题,笔者也是有一段时间思考过怎么不用动脑子的解决这个问题,最后发现不用动脑子的方法的就是把运算符的优先级表打出来(《数据结构教程(第3版)》 唐发根 北京航空航天大学出版社 的栈的应用部分,里面有具体介绍符号的优先级的大小比较和优先级表),打出优先级表之后,这道题简直就是有手就行。
对于表达式树细致构造分析(对笔者启发很大).
参考代码
已经改为在树上做运算
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
/************************** 构建表达式树的函数 ******************/
typedef struct TreeNode{ //树的结构体
int val;
struct TreeNode *left;
struct TreeNode *right;
}Tree,*Treep;
Treep TreeStack[2021],tmp,p,q; //树栈
int OpStack[2021]; //运算符栈
int treeTop=0,opTop=0;
void pushOp( int *s, int item ) //运算符栈压栈
{
s[opTop]=item;
opTop++;
}
void pushTree( Treep *s, Treep item )//树栈压栈
{
s[treeTop]=item;
treeTop++;
}
int popOp( int *s)//运算符栈弹栈
{
return s[--opTop];
}
Treep popTree( Treep *s)//树栈弹栈
{
Treep p=(Treep)malloc(sizeof(Tree));
memcpy(p, s[treeTop-1], sizeof(struct TreeNode)); //复制树节点信息
s[treeTop-1]=NULL; //把树栈里的弹出的节点清空
treeTop--;
return p;
}
int GetOpTop(int *s ) //获得运算符栈的栈顶元素
{
return s[opTop-1];
}
int GetTreeTop(Treep *s ) //获得树栈的栈顶元素
{
return s[treeTop-1]->val;
}
Treep CreateLeaves(int item) //生成一个新的叶子节点
{
Treep q=(Treep)malloc(sizeof(Tree));
q->left=NULL;
q->right=NULL;
q->val=item;
return q;
}
int IsTheta(char c) //判断是否为运算符,是运算符返回1,若不是返回0
{
switch(c){
case '+':
case '-':
case '*':
case '/':
case '(':
case ')':
case '=':
return 1;
default:
return 0;
}
}
char Precede(char theta1,char theta2) //运算符优先级比较
{
int i,j;
char pre[7][7]={// + - * / ( ) =
{'>','>','<','<','<','>','>'},
{'>','>','<','<','<','>','>'},
{'>','>','>','>','<','>','>'},
{'>','>','>','>','<','>','>'},
{'<','<','<','<','<','=','0'},
{'>','>','>','>','0','>','>'},
{'<','<','<','<','<','0','='}};
switch(theta1){
case '+': i=0; break;
case '-': i=1; break;
case '*': i=2; break;
case '/': i=3; break;
case '(': i=4; break;
case ')': i=5; break;
case '=': i=6; break;
}
switch(theta2){
case '+': j=0; break;
case '-': j=1; break;
case '*': j=2; break;
case '/': j=3; break;
case '(': j=4; break;
case ')': j=5; break;
case '=': j=6; break;
}
return(pre[i][j]);
}
int Operate(int a,char theta,int b) //运算符含义表达
{
int result;
switch(theta){
case'+':return a+b;
case'-':return a-b;
case'*':return a*b;
case'/': //判断除数是否为0,若除数为零返回错误提示
if(b!=0)
return a/b;
else
{
printf("Divisor can not Be zero!\n");
exit(0);
}
}
}
//*************************************************构建表达式树
void CreateExpressionTree(char *expression)
{
int theta,X1,X2;
Treep a,b,tmp;
char ch;
int i=0;
pushOp(OpStack,'=');
ch=expression[i++]; // ch 读取表达式的下一个字符
while(ch!='='|| GetOpTop(OpStack)!='=')
{
if(IsTheta(ch)) //判断是否为运算符
{
switch(Precede(GetOpTop(OpStack),ch)) // 比较 ch 和栈顶运算符的优先级
{
case'<':
pushOp(OpStack,ch);
ch=expression[i++];
break;
case'>':
theta=popOp(OpStack);
p=CreateLeaves(theta);
b=popTree(TreeStack);//弹出两个,与新的的节点形成一个子树(倒三角)
a=popTree(TreeStack);
p->left=(Treep)malloc(sizeof(Tree));
p->right=(Treep)malloc(sizeof(Tree));
memcpy(p->left, a, sizeof(struct TreeNode));
memcpy(p->right, b, sizeof(struct TreeNode));
pushTree(TreeStack,p);
break;
case'=':
popOp(OpStack);
ch=expression[i++]; //读取下一位字符并将指针向后偏移一位
break;
}
}
else if(isdigit(ch)) //判断是否为数字
{
X1=ch-'0'; //将字符形式转化为数字
p=CreateLeaves(X1);
pushTree(TreeStack,p);
X2=X1;
ch=expression[i++]; //读取下一位字符并将指针向后偏移一位
while(isdigit(ch)) //判断下一位是否还是数字
{
X1=ch-'0';
X2=10*X2+X1; //归并至X2
tmp=popTree(TreeStack);
p=CreateLeaves(X2);
pushTree(TreeStack,p);
ch=expression[i++]; //读取下一位字符并将指针向后偏移一位
}
}
else if(ch==' ') //判断是否为空格
{
while(ch==' ')
{
ch=expression[i++];
}
}
else //出现非法字符
{
printf("Input has illegal characters!\n");
printf("Please enter again.\n");
exit(0); //返回错误提示
}
}
}
int EvaluateExpression(Treep T)
{
if(T->left&&T->right)
{
if(T->left->left||T->left->right)
EvaluateExpression(T->left);
if(T->right->left||T->right->right)
EvaluateExpression(T->right);
T->val=Operate(T->left->val,T->val,T->right->val);
free(T->left);
free(T->right);
T->left=T->right=NULL;
return T->val;
}
return T->val;
}
void VISIT(Treep t) //访问树节点
{
if(t->val=='/' || t->val=='+' ||t->val=='-' ||t->val=='*') printf("%c ",t->val);
else printf("%d ",t->val);
}
void inorder(Treep t) //中序遍历
{
if(t!=NULL){
inorder(t->left);
VISIT(t); // 访问t指的结点
inorder(t->right);
}
}
void PrintTree(Treep root) //打印树节点信息
{
if(root==NULL) printf("\n");
else
{
if(root->val=='/' || root->val=='+' ||root->val=='-' ||root->val=='*')
printf("%c ",root->val);
else printf("%d ",root->val);
if(root->left!=NULL)
{
if(root->left->val=='/' || root->left->val=='+' ||root->left->val=='-' ||root->left->val=='*')
printf("%c ",root->left->val);
else
printf("%d ",root->left->val);
}
if(root->right!=NULL)
{
if(root->right->val=='/' || root->right->val=='+' ||root->right->val=='-' ||root->right->val=='*')
printf("%c ",root->right->val);
else
printf("%d ",root->right->val);
}
printf("\n");
}
}
int main()
{
char expression[2021];
int result;
gets(expression);
CreateExpressionTree(expression);
//inorder(TreeStack[0]);
PrintTree(TreeStack[0]);
printf("%d\n",EvaluateExpression(TreeStack[0]));
return 0;
}
补充测试的数据
输入
1=
输出
1
1
输入
1+(8/9+8)*(5*7+6)/(4+8)=
输出
+ 1 /
28
输入
(1+3+4*5*(36/9+37))/(7+81/10+3*6)=
输出
/ + +
24
输入
1+2=
输出
+ 1 2
3
题单链接
有考虑过负数和乘方怎么解决吗好兄弟