一、问题描述
编写一个程序,先用二叉树表示一个简单算术表达式,树的每一个 结点包括一个运算符或运算数。在简单算术表达式中只包含+、-、 *、/和一位正整数且格式正确(不包含括号),并且要按照先乘除后 加减的原则构造二叉树。如图 7.35 所示为“1+2*3-4/5”算术表达 式对应的二叉树。然后由对应的二叉树计算该表达式的值。
二、问题解决
#include<stdio.h>
#include<stdlib.h>
#include<string.h> typedef struct BTreeNode
{ char data; struct BTreeNode* lchild; struct BTreeNode* rchild;
} BTNode; float calculate(BTNode* T)
{ if(T==NULL) return 0; if(T->data <='9'&&T->data >='0') return (T->data-'0'); //将 char 类型转化为 int 类型进行
计算 else { switch(T->data) //T->data 为运算符,则有左右孩子节
点且不空 { case'+': return calculate(T->lchild) + calculate(T->rchild);
break; case'-': return calculate(T->lchild) -
calculate(T->rchild); break; case'*': return calculate(T->lchild) *
calculate(T->rchild); break; case'/': return calculate(T->lchild) /
calculate(T->rchild); break; } }
} char* input_cheak_str() //字符串动态输入与检测函数
{ printf("请输入一个简单算术表达式(一位正整数且只有+-*/无括
号):\n"); int ch_num=0; char ch,*str; str=(char*)malloc(sizeof(char)); while((ch=getchar())!='\n') //设置按照输入字符数变化的字符数
组 { if(ch_num%2==1) //下标为奇数,字符应为运算符号 { if(ch!='+' && ch!='-' && ch!='*' && ch!='/') { printf(" 第 %d 个 字 符 输 入 不 合 法 ! 应 为 “ +-*/ ” 之 一
",ch_num+1); return 0; } } else //下标为偶数,字符应该为数字 { if(!(ch>='0' && ch<='9')) { printf("第%d 个字符输入不合法!应为 0 至 9 数字之一
",ch_num+1); return 0; } } str[ch_num]=ch; str=(char*)realloc(str,(++ch_num+1)*sizeof(char)); // ∵
ch_num 为字符数组下标,而 realloc 参数为字符个数 } //∴新
开数组长度参数为下标+2,相当于参数为 num++后的 num+1 if(str[ch_num-1]=='+' || str[ch_num-1]=='-' || str[ch_num-1]=='*' || str[ch_num-1]=='/') { //若最
后一个字符为运算符则输入不合法 printf("最后一个字符输入不合法!应为数字!",ch_num+1); return 0; } str[ch_num]='\0'; //串结尾设置串结束符 return str;
} BTNode* create_tree(char *str)
{ int itemCount=0,ASCount=0,len=strlen(str),i; //AS 意为 addSub 加减
法,itemCount 为加减项计数,ASCount 为加减符号计数 BTNode **ASItem,**ASSign,*root,*p; //ASItem 指针数组存
放加减项节点指针,ASSign 指针数组存放加减符号节点指针 ASItem=(BTNode**)malloc((len/2+1)*sizeof(BTNode*)); ASSign=(BTNode**)malloc((len/2)*sizeof(BTNode*)); if(str[0]=='\0') //加减项节点数应该
为加减符号数加一.所以 itemCount==ASCount+1 return NULL; for(i=0;i<len/2;i++) //指针数组置空 ASSign[i]=NULL; for(i=0;i<len/2+1;i++) ASItem[i]=NULL; for(i=0;i<len;i++) //读取 str 字符数组 { if(str[i]<='9' && str[i]>='0') { p=(BTNode*)malloc(sizeof(BTNode)); p->data=str[i]; p->lchild=p->rchild=NULL; } else if(str[i]=='+'||str[i]=='-') { ASItem[itemCount++]=p; //将 p 节点放入加减项数组 p=(BTNode*)malloc(sizeof(BTNode)); p->data=str[i]; ASSign[ASCount++]=p; } //将加减符号节点指针放入 ASSign 数组,因有符号节点的孩子必不为空且创建过程不会访问其孩子节点,故无需置空 else //str[i]符号为乘除的情况 { root=(BTNode*)malloc(sizeof(BTNode)); root->data=str[i]; //将*,/作为数据存入根节点数据
域 root->lchild=p; //p 一定为数字或*,/节点(都是已
构造好的) p=(BTNode*)malloc(sizeof(BTNode)); p->data=str[++i]; //此时 p 为当前节点的下一个节
点,此时 str[++i]必为数字,且下一个访问的 str 必为符号 p->lchild=p->rchild=NULL; root->rchild=p; //根节点的右孩子连上此节点 p=root; //整个根节点构造完毕,传入 p } } ASItem[itemCount]=p; ASSign[0]->lchild=ASItem[0]; //第一个符号节点左孩子连第一
个项节点 ASSign[0]->rchild=ASItem[1]; for(i=1;i<ASCount;i++) //以加减法符号节点作为子树根
节点,加减法之间的项的节点为子树根节点的孩子节点 { ASSign[i]->lchild=ASSign[i-1]; //除第一个节点以外的加减符号
节点左孩子都连上一个符号节点 ASSign[i]->rchild=ASItem[i+1]; //右孩子都连项节点 } return ASSign[ASCount-1]; } void disp_tree(BTNode *T) { if(T != NULL) { printf("%c", T->data); if(T->lchild != NULL || T->rchild != NULL) { printf("("); // 有孩子结点时才输出( disp_tree(T->lchild); // 递归处理左子树 if(T->rchild != NULL) // 有右孩子结点时才输出, printf(","); disp_tree(T->rchild); // 递归处理右子树 printf(")"); // 有孩子结点时才输出) } } } int main ()
{ char *str=input_cheak_str(); disp_tree(create_tree(str)); printf("\n"); printf("%3.2f",calculate(create_tree(str))); return 0;
三、代码分析
实现简单算术表达式求值,首先要判断输入的表达式是否合法,即只包含 +、-、*、/且正整数的位数为一位。 构建二叉树算法主体思路是将正确(输入时经过检测)的一位正整数无括 号的简单表达式字符串,以加减法为分界线(因为会先计算乘除法)将表达式分 割成若干个加减项后再将已生成的加减项节点与加减符号项节点交替连接成 树,其中每个加减项节点都是其子树。 计算简单算术表达式的值则是要将 char 类型的数字转化为 int 类型的数 字,然后利用 switch 语句进行加减乘除的运算。