任务描述
相关知识
二叉树的二叉链表存储结构
二叉树的先序、中序及后序遍历
先序递归遍历创建二叉树
编程要求
测试说明
任务描述
本关任务:以二叉链表作存储结构存储二叉树,利用先序递归遍历创建二叉树,并依次进行二叉树的前序、中序、后序递归遍历。
相关知识
在顺序存储结构中,利用数组下标表示元素的位置及元素之间孩子或双亲的关系,因此对于非完全二叉树,如果需要增加很多空结点才能将一棵二叉树改造成为一棵完全二叉树,采用顺序存储结构会造成空间的大量浪费,这时不宜用顺序存储结构, 而应该考虑使用链式存储结构。
二叉树的二叉链表存储结构
由于二叉树的每个结点至多有两个孩子,二叉树链式存储结构的每个结点除了需要存储结点本身的信息之外,还需要存储结点左右孩子的地址,二叉树的链式存储中的结点结构如图所示:
其中,lchild和rchild是分别指向该结点左孩子和右孩子的指针,data是数据元素的内容,这种结构称为二叉链表。结点的类型声明如下:
typedef struct BiTNode
{
TElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode ,*BiTree;
data表示数据域,用于存储放入结点值。
lchild和rchild分别表示左指针域和右指针域,分别存储左孩子和右孩子结点(即左、右子树的根结点)的存储地址。
当某结点不存在左或右孩子时,其lchild或rchild指针域为NULL。
本实训任务定义data为char型:
typedef char TElemType ;
例:画出下列二叉树的二叉链表存储结构 :
二叉树的先序、中序及后序遍历
二叉树的遍历是指按一定的次序访问树中的所有结点,使每个结点恰好被访问一次。其中遍历次序保证了二叉树上每个结点均被访问一次且仅有一次。
二叉树常用的遍历有先序(根)遍历、中序(根)遍历、后序(根)遍历。
因为二叉树的定义就是递归定义,因此采用递归的方法去实现二叉树的三种遍历,所谓先序、中序、后序,区别在于访问根结点的顺序。
1.先序遍历
若二叉树非空,则:
① 访问根结点;
② 先序遍历左子树;
③ 先序遍历右子树。
先序遍历序列的特点:其第一个元素值为二叉树中根结点值。
对应的递归算法实现:
void ProOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ // 采用二叉链表存储结构,Visit是对数据元素操作的应用函数。
// 先序遍历二叉树T的递归算法,对每个数据元素调用函数Visit
if(T) // T不空
{
Visit(T->data); // 访问根结点
ProOrderTraverse(T->lchild,Visit); // 先序遍历左子树
ProOrderTraverse(T->rchild,Visit); // 先序遍历右子树
}
}
2.中序遍历
若二叉树非空,则:
① 中序遍历左子树;
② 访问根结点;
③ 中序遍历右子树。
中序遍历序列的特点:若已知二叉树的根结点值,以该值为界,将中序遍历序列分为两部分,前半部分为左子树的中序遍历序列,后半部分为右子树的中序遍历序列。
请大家思考中序递归遍历算法与先序递归算法区别与联系。
3.后序遍历
若二叉树非空,则:
① 后序遍历左子树;
② 后序遍历右子树;
③ 访问根结点。
后序遍历序列的特点:最后一个元素值为二叉树中根结点值。
请大家类推后序递归遍历算法。
用#字符表示‘无孩子’或指针为空,指针为空时是递归算法的出口。
例:下列二叉树的遍历结果:
例:下列二叉树的遍历结果:
前序遍历:ABC##D##EF###
中序遍历:#C#B#D#A#F#E#
后序遍历:##C##DB##F#EA
注意:如果不输出空树,则图1二叉树遍历序列如下:
前序遍历:ABCDEF
中序遍历:CBDAFE
后序遍历:CDBFEA
二叉树最重要的运算是:遍历,解决二叉树的很多问题的方案都是基于对二叉树的遍历。
如何将一颗二叉树以二叉链表形式存入计算机内。
下面以先序递归遍历思想实现二叉树的创建。
先序递归遍历创建二叉树
递归法
在定义一个过程或函数时出现调用本过程或本函数的成分,称之为递归。
一般地,递归模型由两部分组成,一部分为递归出口,它给出了递归的终止条件。另一部分为递归体,它确定递归求解时的递推关系。
先序递归遍历创建二叉树
利用递归思想,首先创建根结点,再先序创建左子树,再先序创建右子树。
先创建根,扫描字符序列,读入字符ch,如果ch是一个“#”字符,则表明该二叉树为空树,即T为NULL;否则执行以下操作:
申请一个节点空间T
将ch赋给T->data
先序递归创建T的左子树;
先序递归创建T的左子树。
当读入的是一个“#”字符时,是递归的出口。
思考:可否用中序递归遍历或后序递归遍历序列创建二叉树?
编程要求
根据提示,在右侧编辑器补充代码。
InitBiTree(BiTree &T)//构造空二叉树T
CreateBiTree(BiTree &T)//按先序遍历次序输入二叉树中结点的值(字符型或整型), 构造二叉链表存储的二叉树T
PreOrderTraverse(BiTree T,void(*Visit)(TElemType))//先序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
InOrderTraverse(BiTree T,void(*Visit)(TElemType))//中序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
PostOrderTraverse(BiTree T,void(*Visit)(TElemType))//后序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
void DestoryBiTree(BiTree &T)// 销毁二叉树
#define ClearBiTree DestroyBiTree // 清空二叉树和销毁二叉树的操作一样
测试说明
平台会对你编写的代码进行测试:
测试输入:
ABD##FE###CG#H##I##
预期输出:
先序遍历为:A,B,D,F,E,C,G,H,I,
中序遍历为:D,B,E,F,A,G,H,C,I,
后序遍历为:D,E,F,B,H,G,I,C,A,
开始你的任务吧,祝你成功!
#include <stdio.h>
#include <stdlib.h>
#include "bitree.h"
TElemType Nil='#';
void visit(TElemType s)
{
printf("%c,",s);
}
void input(TElemType &s)
{
scanf("%c",&s);
}
void CreateBiTree(BiTree &T)
{ //按先序次序输入二叉树中结点的值
// 构造二叉链表表示的二叉树T。变量Nil表示空(子)树。
/********** Begin **********/
T=(BiTree)malloc(sizeof(BiTree));
TElemType tem;
input(tem);
if(tem=='#')
{
T=NULL;
return;
}
T->data=tem;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
/********** End **********/
}
int BiTreeEmpty(BiTree T)
{ // 初始条件:二叉树T存在。操作结果:若T为空二叉树,则返回TRUE,否则FALSE
/********** Begin **********/
if(T)
return 0;
else
return 1;
/********** End **********/
}
void ProOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ // 采用二叉链表存储结构,Visit是对数据元素操作的应用函数。
// 先序遍历二叉树T的递归算法,对每个数据元素调用函数Visit
/********** Begin **********/
if(T) // T不空
{
Visit(T->data); // 访问根结点
ProOrderTraverse(T->lchild,Visit); // 先序遍历左子树
ProOrderTraverse(T->rchild,Visit); // 先序遍历右子树
}
/********** End **********/
}
void InOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ // 采用二叉链表存储结构,Visit是对数据元素操作的应用函数。
// 中序遍历二叉树T的递归算法,对每个数据元素调用函数Visit
/********** Begin **********/
if(T)
{
InOrderTraverse(T->lchild,Visit);
Visit(T->data);
InOrderTraverse(T->rchild,Visit);
}
/********** End **********/
}
void PostOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ // 初始条件:二叉树T存在,Visit是对结点操作的应用函数
// 操作结果:后序递归遍历T,对每个结点调用函数Visit一次且仅一次
/********** Begin **********/
if(T) // T不空
{
PostOrderTraverse(T->lchild,Visit); // 后序遍历左子树
PostOrderTraverse(T->rchild,Visit); // 后序遍历右子树
Visit(T->data); // 访问根结点
}
/********** End **********/
}
void DestoryBiTree(BiTree &T)
{ // 初始条件:二叉树T存在。操作结果:销毁二叉树T
/********** Begin **********/
if(T)
{
DestoryBiTree(T->lchild);
DestoryBiTree(T->rchild);
free(T);
}
/********** End **********/
}
2552

被折叠的 条评论
为什么被折叠?



