《数据结构与算法》实验二: 二叉树的遍历
一、目的和要求(需求分析)
1、掌握二叉树的存储结构以及二叉树的建立和操作。
2、输入一串表达式后,建立二叉树,并对其进行先序、中序和后序的遍历。
(输入表达式如此形式:a+b*c-d-e/f….;以#号结束。)
3、递归实现表达式运算。
二、程序设计的基本思想,原理和算法描述
(包括程序的结构,数据结构,输入/输出设计,符号名说明等)
1. 定义二叉树的结点结构
结构:用flag记录所输入内容为数值还是符号,若为数值则flag=1,并将内容存入num中;若为符号则flag=0,并将内容存入data中。
2. 利用递归建立二叉树引例
用一个表达式3*(1+2)+2为例,建树时先去寻找最后使用的操作符“+”作为根节点,然后对“+”两边表达式分别继续判断找出该段中的最后使用操作符,以此类推。很明显,若有括号存在,括号外的符号先建树,靠近根节点。故可以在其他部分判断完后,再考虑括号内的部分。
基本思路:找到表达式中最后使用的操作符,作为二叉树根节点,左侧右侧递归生成的二叉树分别为左孩子和右孩子。按照括号、乘除、加减顺序比较优先级,建立二叉树状结构时优先级越低越靠近根节点。
数据结构:利用二叉链表存储二叉树。
3. 输入:输入需要计算的表达式(以#作为结尾标志)
4. 利用递归遍历二叉树
要求:先序、中序、后序遍历显示二叉树。
5. 利用递归计算结果
递归规律:表达式树的值=树根结点左子树值与右子树的值通过根结点存储的运算符运算得到的结果。
递归出口:表达式树为叶子结点。
三、调试和运行程序过程中产生的问题及采取的措施
1. 问题:不知道如何将表达式建立成二叉树。
措施:找到表达式中最后使用的操作符,将其作为二叉树的根节点,再对左侧、右侧分别递归生成的二叉树作为其左孩子和右孩子,实现表达式到二叉树的转化。
2. 问题:不知道如何判断优先级。
措施:按照括号、乘除、加减顺序比较先行级,建立二叉树状结构时先行级越低越靠近根节点。先判断括号外的部分,再考虑括号内的部分。
定义一个f1判断当前字符是否在括号内,当f1遇到左括号加1,遇到右括号减1;若在括号内则f1=1,否则f1=0。用j记录加减符号位置,c记录乘除符号位置。当f1=0时表示已经跳出括号,若有j则s[j]是所要找的优先级最低的符号,否则为s[c]。
3. 问题:多位数运算的实现。
刚开始写代码时,只考虑了一位数运算,导致当输入多位数运算表达式时报错。后来在源代码基础上改进,增加了一个变量f2判断当前所剩区域是否均为数值,若均为数值,则将此段字符转为数字并放入树中。
但是当修改完后,代码一直运行不出结果,于是就询问老师和同学。最后,发现是建树时判断顺序出现了问题。
四、源程序及注释
#include<stdio.h>
#include<stdlib.h>
//定义二叉树的结点结构
typedef struct BiTNode {
int flag; //判断是数字还是符号,若为数字flag=1,若为符号flag=0
char data;
int num;
struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTNode,*BiTree;
BiTree CreateBiT(char s[],int start,int y);
void PreOrderTraverse(BiTree T);
void InOrderTraverse(BiTree T);
void PostOrderTraverse(BiTree T);
void DestroendTree(BiTree T);
double count(BiTree T);
//以递归方式建立表达式的二叉树状结构
BiTree CreateBiT(char s[],int start,int end)
{
if(end<start) return 0;
{
//按照括号、乘除、加减顺序比较先行级,建立二叉树状结构时先行级越低越靠近根节点
int j=0,c=0,k=0,f1=0,f2=0;
double q=0;
//j记录加减符号位置,c记录乘除符号位置
//用f1判断是否为括号外区域,f1=0时在括号外
//用f2判断当前区域是否只剩下数值
for(int i=start;i<end;i++)
{
if(s[i]=='(') {f1++;f2++;}
else if (s[i] == ')') {f1--;f2++;}
if(f1==0)
{
if (s[i]=='+'||s[i]=='-') {j=i;f2++;}
else if (s[i]=='*'||s[i]=='/') {c=i;f2++;}
}
}
if(f2==0) //只剩下数值
{
for(int i=start;i<end;i++) q=q*10+s[i]-'0';
BiTree p=(struct BiTNode*)malloc(sizeof(struct BiTNode));
p->flag=1;
p->num=q;
p->lchild=NULL;
p->rchild=NULL;
return p;
}
else if(j==0&&c==0) //开头与结尾就是括号
{
BiTree y=(struct BiTNode*)malloc(sizeof(struct BiTNode));
y=CreateBiT(s,start+1,end-1);
return y;
}
else
{
if(j>0) k=j; //记录加减符号所在位置
else if(c>0) k=c; //记录乘除符号所在位置
BiTree p=(struct BiTNode*)malloc(sizeof(struct BiTNode));
p->flag=0;p->data=s[k];
p->lchild =CreateBiT(s,start,k);
p->rchild = CreateBiT(s,k+1, end);
return p;
}
}
}
//前序遍历
void PreOrderTraverse(BiTree T)
{
if(T)
{
if(T->flag==1) printf("%d ",T->num);
else if(T->flag==0) printf("%c ",T->data);
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
}
//中序遍历
void InOrderTraverse(BiTree T)
{
if(T)
{
InOrderTraverse(T->lchild);
if(T->flag==1) printf("%d ",T->num);
else if(T->flag==0) printf("%c ",T->data);
InOrderTraverse(T->rchild);
}
}
//后序遍历
void PostOrderTraverse(BiTree T)
{
if(T)
{
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
if(T->flag==1) printf("%d ",T->num);
else if(T->flag==0) printf("%c ",T->data);
}
}
//计算结果
double count(BiTree T)
{
double sum=0;
if(T->flag==0)
{
if (T->data=='+')
sum=count(T->lchild)+count(T->rchild);
else if (T->data == '-')
sum=count(T->lchild)-count(T->rchild);
else if (T->data == '*')
sum=count(T->lchild)*count(T->rchild);
else if (T->data == '/')
sum=count(T->lchild)*1.0/count(T->rchild);
}
else sum=T->num;
return sum;
}
// 摧毁表达式树
void DestroendTree(BiTree T)
{
if (T) {
DestroendTree(T->lchild);
DestroendTree(T->rchild);
free(T);
}
}
int main()
{
char s[200],ch;
printf("请输入表达式(以#为结尾):\n");
int l=0;
double sum=0;
while((ch = getchar())!='#')
{
s[l] = ch;
l++;
}
BiTree p= (struct BiTNode*)malloc(sizeof(struct BiTNode));
p=CreateBiT(s,0,l);
printf("先序遍历结果为:");
PreOrderTraverse(p);
printf("\n");
printf("中序遍历结果为:");
InOrderTraverse(p);
printf("\n");
printf("后序遍历结果为:");
PostOrderTraverse(p);
printf("\n");
printf("表达式计算结果为:");
sum=count(p);
printf("%.2f\n",sum);
DestroendTree(p);
}
五、运行输出结果
六、心得与体会
这一次的实验让我对二叉树的存储结构、建立及操作有了更深的认识。定义二叉树存储结构时,了解了顺序存储和链式存储;实现二叉树遍历时,掌握了前序、中序、后序遍历;计算表达式的结果时,知道了要搞清楚符号的优先级。多位数运算的实现是个难点,通过问老师、问同学、网上查阅资料,花了很长时间才解决,这也让我明白了要静下心去琢磨才能学到东西。