目录
前言
在学习了二叉树之后,对二叉树的概念还处于比较懵的状态,下面就一项实验报告来具体实现二叉树,目的是为了掌握二叉树的定义,并且实现二叉树的链式存储结构以及在链式存储结构中的三种遍历(前序、中序、后序)。
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是二叉树?
二叉树(binary tree)是一个有限的结点集合,这个集合或则为空,或则由一个根结点和两颗互不相交的称为左子树(left subtree)和右子树(right subtree)的二叉树组成。
二叉树的抽象数据类型描述和树的抽象数据类型相似,这里不再介绍。显然,和树的定义一样,二叉树的定义也是一个递归定义。二叉树的结构简单、存储效率高,其运算算法也相对简单,而且任何m次数都可以转化为二叉树结构,可见二叉树的重要性。
二、实验内容
1.题目
1. 编写程序,建立一棵二叉树(以链表存储),对该二叉树进行遍历并输出该二叉树的前序,中序,后序遍历序列;
2. 编写程序,建立一棵二叉树(以链表存储),实现二叉树左右子树的交换;
3. 统计二叉树中叶子结点个数。
2.具体实现
(1)首先创建一个二叉树的结构体,具体实现如下:
typedef struct node
{
ElemType data;//存放结点值
struct node *lchild;//左子树
struct node *rchild;//右子树
}BTNode;
(2)创建一个二叉树
k的数值控制的是左右子树,k=1为左子树,k=2为右子树;因为str是主函数传入的字符串,所以j用来一个一个读入字符串中的单个字符;
void CreateBTree(BTNode *&b,char *str)//创建二叉树
{
BTNode *St[MaxSize],*p; //St数组作为顺序栈
int top=-1,k,j=0; //top为栈顶指针
char ch;
b=NULL; //初始时二叉链为空
ch=str[j];
while(ch!='\0') //循环扫描str中的每个字符
{
switch(ch)
{
case '(':top++;St[top]=p;k=1;break; //开始处理左孩子结点
case ',':k=2;break; //开始处理右孩子结点
case ')':top--;break; //栈顶结点的子树处理结束
default:
p=(BTNode *)malloc(sizeof(BTNode)); //创建树的根节点
p->data =ch; //存放结点值
p->lchild=p->rchild=NULL; //左右指针域置为空
if(b==NULL)
b=p;
else //已建立二叉树的根结点
{
switch(k)
{
case 1:St[top]->lchild=p;break;//新建结点作为栈顶结点的左孩子
case 2:St[top]->rchild=p;break;//新建结点作为栈顶结点的右孩子
}
}
}
j++; //继续扫描str
ch=str[j];
}
}
(3)实现三种遍历方式:
先序遍历:(NLR) 中序遍历:(LNR) 后序遍历(LRN)
1、访问根结点 1、中序遍历左子树 1、后序遍历左子树
2、先序遍历根结点 2、访问根结点 2、后序遍历右子树
3、先序遍历右结点 3、中序遍历右子树 3、访问根结点
通过下面的代码可以发现三种遍历的框架是相同的,唯一不同的是访问根结点的顺序不同,第一步访问根结点的为先序遍历,第二步访问根结点的为中序遍历,第三步访问根结点的为后序遍历。
void PreOrder(BTNode *b)//先序遍历
{
if(b!=NULL)
{
printf("%c",b->data);//访问根结点
PreOrder(b->lchild);//先序遍历左子树
PreOrder(b->rchild);//先序遍历右子树
}
}
void InOrder(BTNode *b)//中序遍历
{
if(b!=NULL)
{
InOrder(b->lchild);//中序遍历左子树
printf("%c",b->data);//访问根结点
InOrder(b->rchild);//中序遍历右子树
}
}
void PostOrder(BTNode *b)//后序遍历
{
if(b!=NULL)
{
PostOrder(b->lchild);//后序遍历左子树
PostOrder(b->rchild);//后序遍历右子树
printf("%c",b->data);//访问根结点
}
}
(4)叶子结点的个数:
下面的代码通过递归实现求叶子节点的个数,if((!b->lchild)&&(!b->rchild)) return 1;作为递归出口。Leaf(b->lchild)+Leaf(b->rchild)实现不断的递归,本实验求的是叶子节点个数,如若求得是结点个数,直接在 Leaf(b->lchild)+Leaf(b->rchild) 式子上加1即可,其余部分不做改变。
int Leaf(BTNode *b)//求叶子节点
{
if(!b) //判断树是否为空
return 0;
else //不为空是进行下一步
{
if((!b->lchild)&&(!b->rchild))
return 1;
else
return Leaf(b->lchild)+Leaf(b->rchild);
}
}
(5)二叉树的子树交换
void changeTree(BTNode *b)//交换二叉树的子树
{
if(b==NULL)
cout<<"树为空,输出失败"<<endl;
else
{
BTNode *temp;
temp=b->lchild;
b->lchild=b->rchild;
b->rchild=temp;
cout<<"树的左右子树交换成功"<<endl;
}
}
如上述代码,我在实验过程中,刚开始只考虑到交换了根结点的左右子树,而忽视了实验的要求是交换所有的左右子树,只创建了一个中间变量进行交换。
下面是改进了代码后的子树交换,创建了两个中间变量分别用于控制左子树和右子树,再利用递归巧妙地进行所有子树的交换。
void changeTree(BTNode *b)//交换二叉树的子树
{
if(b!=NULL)
{
if(b->lchild!=NULL||b->rchild!=NULL)
{
BTNode *p,*q;
p=b->lchild;
q=b->rchild;
b->lchild=q;
b->rchild=p;
changeTree(b->lchild);
changeTree(b->rchild);
}
}
}
(6)主函数的具体实现:
int main()
{
BTNode *tree;
CreateBTree(tree,"a(b,c(d,e))");
cout<<"前序遍历为:";
PreOrder(tree);
cout<<endl;
cout<<"中序遍历为:";
InOrder(tree);
cout<<endl;
cout<<"后序遍历为:";
PostOrder(tree);
cout<<endl;
cout<<"叶子结点个数为:";
int number=Leaf(tree);
cout<<number<<endl;
changeTree(tree);
cout<<"交换后的二叉树为:"<<endl;
cout<<"前序遍历为:";
PreOrder(tree);
cout<<endl;
cout<<"中序遍历为:";
InOrder(tree);
cout<<endl;
cout<<"后序遍历为:";
PostOrder(tree);
cout<<endl;
return 0;
}