/**
* 实验题目:
* 简单算术表达式二叉树的构建和求值
* 实验目的:
* 掌握二叉树遍历算法的应用,熟练使用先序、中序、后序3种递归
* 遍历算法进行二叉树问题求解。
* 实验内容:
设计程序,先用二叉树来表示一个简单算术表达式,树的每一个结点
* 包括一个运算符或运算数。简单算术表达式中只包含+、-、*、/和一位正
* 整数且格式正确(不包含括号),并且要按照先乘除后加减的原则构造二叉树。
* 构造1+2*3-4/5代数表达式对应的二叉树,然后由对应的二叉树计算该表达式的值。
*/
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#define MAX_SIZE 100
typedef char ElemType;
typedef struct node
{
ElemType data; // 数据元素
struct node *lchild; // 指向左孩子结点
struct node *rchild; // 指向右孩子结点
}BTNode; // 声明二叉链结点类型
/*--------------------------以括号表示法输出二叉树b----------------------*/
static void disp_btree(BTNode *b)
{
if(b != NULL)
{
printf("%c", b->data);
if(b->lchild != NULL || b->rchild != NULL)
{
printf("("); // 有孩子结点时才输出(
disp_btree(b->lchild); // 递归处理左子树
if(b->rchild != NULL) // 有右孩子结点时才输出,
printf(",");
disp_btree(b->rchild); // 递归处理右子树
printf(")"); // 有孩子结点时才输出)
}
}
}
/*--------------------------释放二叉树b的所有结点----------------------*/
static void destroy_btree(BTNode *&b) // 销毁二叉树(形参b:指针的引用)
{
if(b != NULL)
{
destroy_btree(b->lchild);
destroy_btree(b->rchild);
free(b);
}
}
/*--------------------------建立简单算术表达式s[i...j]对应的二叉树----------------------*/
/**
* @功能:用于生成简单算术表达式对应的二叉树,递归函数。
* @param s:存放简单算术表达式字符串
* @i:该字符串或其子串的起始位置
* @j:该字符串或其子串的终止位置
* 如果i=j,说明子串只有一个字符,即为叶子结点,则创建只有一个根结点的二叉树并返回。
* 如果i≠j,根据运算规则,在串中找"+"或"-"号,以最后的"+"或"-"为根(体现从左到右的原则);
* 当没有"+"或"-"号时,则进一步找"*"或"/"(体现先乘除后加减的原则),同样以最后的运算符
* 为根,将串分为两部分,即左子树和右子树。创建一个根结点,将找到的运算符放入,递归
* 调用自身进入左子树的建树工作,之后递归调用自身进入右子树的建树工作。
*/
static BTNode *cr_tree(char s[], int i, int j)
{
int oper_num = 0; // 记录运算符的个数
BTNode *p;
int k;
int pos;
if(i == j) // 处理i=j的情况,说明只有一个字符
{
p = (BTNode *)malloc(sizeof(BTNode));
p->data = s[i];
p->lchild = p->rchild = NULL;
return p;
}
/*---------------------以下为i≠j的情况-----------------------*/
for(k = i; k <= j; k++) // 首先查找+和-运算符
{
if(s[k] == '+' || s[k] == '-')
{
oper_num++; // oper_num记录+或者-的个数
pos = k; // pos记录最后一个+或者-的位置
}
}
if(oper_num == 0) // 没有+或者-的情况
{
for(k = i; k <= j; k++)
{
if(s[k] == '*' || s[k] == '/')
{
oper_num++; // oper_num记录*或者/的个数
pos = k; // pos记录最后一个*或者/的位置
}
}
}
if(oper_num != 0) // 有运算符的情况,创建一个存放它的结点
{
p = (BTNode *)malloc(sizeof(BTNode));
p->data = s[pos];
p->lchild = cr_tree(s, i, pos - 1); // 递归处理s[i...pos - 1]构造左子树
p->rchild = cr_tree(s, pos + 1, j); // 递归处理s[pos + 1, j]构造右子树
}
else // 没有任何运算符,返回NULL
return NULL;
}
/*--------------------------计算二叉树对应表达式的值----------------------*/
/**
* @功能:计算二叉树对应表达式的值,递归函数。
* 若为空树,则返回0,否则若b所指结点为叶子结点,则返回其data值,否则求出
* 左子树的值v1,再求出右子树的值v2,根据b所指结点的运算符对v1和v2进行相应
* 的计算并返回计算后的结果。
*/
static double compute(BTNode *b)
{
double v1, v2;
if(b == NULL)
return 0;
if(b->lchild == NULL && b->rchild == NULL) // 叶子结点直接返回结点值
return b->data - '0';
v1 = compute(b->lchild); // 递归求出左子树的值v1
v2 = compute(b->rchild); // 递归求出右子树的值v2
switch(b->data) // 根据b结点做相应运算
{
case '+':
return v1 + v2;
case '-':
return v1 - v2;
case '*':
return v1 * v2;
case '/':
if(v2 != 0)
return v1 / v2;
else
abort(); // 除0异常退出
}
}
int main(int argc, char *argv[])
{
BTNode *b;
char s[MAX_SIZE] = "1+2*3-4/5";
printf("算术表达式%s\n", s);
b = cr_tree(s, 0, strlen(s) - 1);
printf("对应二叉树:");
disp_btree(b);
printf("\n算术表达式的值:%g\n", compute(b));
destroy_btree(b);
return 0;
}
测试结果:
算术表达式1+2*3-4/5
对应二叉树:-(+(1,*(2,3)),/(4,5))
算术表达式的值:6.2