《数据结构》课程设计二叉树操作.docx
- 文档编号:28614238
- 上传时间:2023-07-19
- 格式:DOCX
- 页数:40
- 大小:142.04KB
《数据结构》课程设计二叉树操作.docx
《《数据结构》课程设计二叉树操作.docx》由会员分享,可在线阅读,更多相关《《数据结构》课程设计二叉树操作.docx(40页珍藏版)》请在冰豆网上搜索。
《数据结构》课程设计二叉树操作
《数据结构》课程设计报告
题目
二叉树的操作
学号
姓名
班级
专业
学院
信息科学技术学院
指导教师
戴小鹏
日期
2014年12月22日
一需求分析:
1.1问题描述
实现二叉树(包括二叉排序树)的建立,并实现先序、中序、后序和按层次遍历,计算叶子结点数、树的深度、树的宽度,求树的非空子孙结点个数、度为2的结点数目、度为2的结点数目,以及二叉树常用运算。
1.2基本要求
(1)输入的形式和输入值的范围;
为了方便操作,规定二叉树的元素类型都为字符型,允许各种字符类型的输入,没有元素的结点以空格输入表示,并且本实验是以先序顺序输入的。
(2)输出的形式;
通过先序、中序和后序遍历的方法对树的各字符型元素进行遍历打印,再以广义表形式进行打印。
对二叉树的一些运算结果以整型输出。
(3)程序所能达到的功能;
1、建立二叉树;
2、通过递归方法来遍历(先序、中序和后序)二叉树;
3、通过队列应用来实现对二叉树的层次遍历;
4、借用递归方法对二叉树进行一些基本操作,如:
求叶子数、树的深度宽度等;
运用广义表对二叉树进行广义表形式的打印。
二概要设计
1、设定“二叉树”的抽象数据类型定义:
ADTBiTree{
数据对象D:
D是具有相同特性的数据元素的集合。
数据关系R:
若
,则
,称BiTree为空二叉树;
若
,则
,H是如下二元关系:
在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
若
则存在
且
;
若
则
中存在唯一的元素
且存在
上的关系
若
则
中存在唯一的元素
且存在
上的关系
;
是一棵符合本定义的二叉树,称为根的左子树,
是一棵符合本定义的二叉树,称为根的有字树
基本操作:
CreateBiTree&T)操作结果:
按照T的定义构造一个二叉树。
BiTreeDepth(&T)初始条件:
二叉树T已存在。
操作结果:
返回T的深度。
BiTreeWidth(&T)初始条件:
二叉树T已存在。
操作结果:
返回T的宽度。
PreOderTraverse(&T)初始条件:
二叉树T已存在。
操作结果:
先序遍历打印
InOderTraverse(&T)初始条件:
二叉树T已存在。
操作结果:
中序遍历打印T。
PostOderTraverse(&T)初始条件:
二叉树T已存在。
操作结果:
后序遍历打印T。
LevelTraverse(&T)初始条件:
二叉树T已存在。
操作结果:
层次遍历T。
DeleteXTree(&T,TElemTypex:
初始条件:
二叉树已存在。
操作结果:
删除元素为x的结点以及其左子树和右子树。
CopyTree(&T):
初始条件:
二叉树T已存在。
操作结果:
以T为模板,复制另一个二叉树。
}ADTBiTree
2、设定队列的抽象数据类型定义:
ADTQueue{
数据对象:
D={
数据关系:
R1={<
>|
i=2,…,n}
约定
端为队列头,
端为队列尾。
基本操作:
InitQueue(&Q)操作结果:
构造一个空队列Q。
EnQueue(&Q,&e)初始条件:
队列Q已存在。
操作结果:
插入元素e为Q的新的队尾元素。
DeQueue(&Q)初始条件:
队列Q已存在。
操作结果:
删除Q的对头元素,并返回其值。
QueueEmpty(&Q)初始条件:
队列Q已存在。
操作结果:
若Q为空队列,则返回1,否则0。
}ADTQueue
3、本程序包含四个模块
1)主程序模块
voidmain()
{
初始化;先序输入二叉树各结点元素;各种遍历二叉树;对二叉树进行常见运算;复制二叉树;在复制的二叉树上进行二叉树的常见操作;
}
2)二叉树模块——实现二叉树的抽象数据类型和基本操作
3)队列模块——实现队列的抽象数据类型及今本操作
4)广义表打印模块——以广义表形式打印二叉树
5)二叉树运算模块——对二叉树的叶子、非空子孙结点数目、度为2或1的结点数
详细设计
1、主程序中需要的全程量
#defineM10//统计二叉树宽度时需用数组对每层宽度进行存储,这M表示数组的长度
2、队列的建立与基本操作
typedefstrucQNodte{
BiTreedata;//队列中存储的元素类型
structQNode*next;//指向二叉树结点的指针
}QNode,*Queueptr;
typedefstruct{
Queueptrfront;//队列的队头指针
Queueptrrear;//队列的队尾指针
}LinkQueue;
算法描述:
为了对二叉树进行层次遍历,需要“先访问的结点,其孩子结点必然也先访问”,故需运用到队列。
而考虑到层次遍历对队列操作的需要,只需进行队列的初始化,入队和出队以及检查队列是否为空。
伪码算法:
voidInitQueue(LinkQueue*Q)
{//初始化队列申请一个结点
Q->front=Q->rear=(Queueptr)malloc(sizeof(QNode));
if(!
Q->front)
exit
(1);//存储分配失败处理
Q->front->next=NULL;//构成带头结点里的空链队列
}
voidEnQueue(LinkQueue*Q,BiTreee)
{//插入元素为e为Q的队尾元素
Queueptrq;
q=(Queueptr)malloc(sizeof(QNode));
if(!
q)
exit
(1);
q->data=e;
q->next=NULL;
Q->rear->next=q;//元素e的结点入列
Q->rear=q;
}
BiTreeDeQueue(LinkQueue*Q)
{//从队列中删除队头元素,并直接返回给调用的主函数
Queueptrp;
BiTreeq;
if(Q->front==Q->rear)
{//队列为空
printf("ERROR!
\n");
exit
(1);
}
p=Q->front->next;
q=p->data;
Q->front->next=p->next;//删除该结点
if(Q->rear==p)
Q->rear=Q->front;
free(p);
returnq;//返回队头元素
}
intQueueEmpty(LinkQueue*Q)
{//检查队列是否为空,若为空则返回真值,否则返回0
if(Q->front==Q->rear)
return1;
else
return0;
}
3、二叉树的建立与基本操作
typedefstructBiTNode{
TElemTypedata;//二叉树结点存储的元素类型
structBiTNode*lchild,*rchild;//二叉树左右孩子指针
}BiTNode,*BiTree;
算法分析:
二叉树的基本操作是本程序的核心,考虑到二叉树的定义就是采用递归定义,所以很容易想到对二叉树的操作可通过递归调用来实现。
1)二叉树的遍历
算法描述:
二叉树中常常要求在树中查找具有某种特征的结点,或者对树中全部结点逐一进行某种处理,这就需要遍历二叉树。
即要求按某条路径寻访树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。
先序遍历二叉树的操作:
伪码算法:
voidPreOderTraverse(BiTreeT)
{//采用二叉链表存储结构
if(T)
{
putchar(T->data);//最简单的访问结点法,输出结点元素,这里先访问根结点
PreOderTraverse(T->lchild);
PreOderTraverse(T->rchild);
}
}
中序遍历二叉树的操作:
伪码算法:
voidInOderTraverse(BiTreeT)
{//采用二叉链表存储结构
if(T)
{
InOderTraverse(T->lchild);//先遍历左子树
putchar(T->data);//最简单的访问结点法,输出结点元素,这里第二访问根结点
InOderTraverse(T->rchild);
}
}
后序遍历二叉树的操作:
伪码算法:
voidPostOderTraverse(BiTreeT)
{//采用二叉链表存储结构
if(T)
{
PostOderTraverse(T->lchild);//先遍历左子树
PostOderTraverse(T->rchild);//再遍历右子树
putchar(T->data);//最后访问根结点
}
}
2)二叉树的建立
算法描述:
对二叉树“遍历”基本操作已经建立的基础上,可以在便利过程中对结点进行各种操作,如:
在遍历过程中生成结点,建立二叉树存储结构。
采用先序遍历建立二叉树链表:
按先序序列输入二叉树中结点的元素,以空格表示空,直到输入回车为止;若元素值为空,则赋值NULL;否则生成一个新的结点,将元素值赋值给该跟结点的数值域;建立该跟结点的左子树;建立该根结点的右子树。
伪码算法;
BiTreeCreateBiTree(BiTreeT)
{//构造二叉树T,按先序序列输入二叉树中结点的值(一个字符),空格表示空树
charch;
if((ch=getchar())=='')
T=NULL;
else
if(ch!
='\n')
{
if(!
(T=(BiTree)malloc(sizeof(BiTNode))))
exit
(1);
T->data=ch;//生成根结点
T->lchild=CreateBiTree(T->lchild);//构造左子树
T->rchild=CreateBiTree(T->rchild);//构造右子树
}
returnT;//返回所构造二叉树(子)树的地址
}
3)二叉树的层次遍历
算法分析:
有时需要一层层访问二叉树,比如判断二叉树是否为完全二叉树、找出指定层中含有的叶子/度为2/度为1的结点等,这就需要另一种遍历方法——层次遍历。
先访问的结点,其孩子结点必然也先访问,引入队列存储已被访问的,但其左右孩子尚未访问的结点的指针。
算法描述:
(1)若结点不为空,则访问该结点,并将其入列;
(2)接着从队列中取已被访问的,但其左右孩子尚未被访问的;
(3)访问该结点的左孩子,将其入列;
(4)访问该结点的右孩子,将其入列。
伪码算法:
intvisit(TElemTypee)
{//简单的结点访问函数
if(e)
{
putchar(e);//打印该结点元素
return1;
}
else
return0;
}
voidLevelTraverse(BiTreeT)
{
LinkQueue*Q=(LinkQueue*)malloc(sizeof(LinkQueue));
BiTreep=(BiTree)malloc(sizeof(BiTNode));
InitQueue(Q);//初始化队列,队列的元素为二叉树的结点指针
if(T!
=NULL)
{
if(!
visit(T->data))//访问该结点
exit
(1);
EnQueue(Q,T);//将已被访问的,但左右孩子没被访问的结点入列
while(!
QueueEmpty(Q))
{//队列不为空,则尚有未被访问其孩子的结点
p=DeQueue(Q);//将该结点出列,返回其地址
if(p->lchild!
=NULL)
{
if(!
visit(p->lchild->data))//访问左孩子
exit
(1);
EnQueue(Q,p->lchild);//将其入列
}
if(p->rchild!
=NULL)
{
if(!
visit(p->rchild->data))//访问右孩子
exit
(1);
EnQueue(Q,p->rchild);//将其入列
}
}
}
}
4)求二叉树的深度
算法描述:
若结点不为空,则求其左子树的深度,并返回其值h1;求其右子树的深度,也返回其值h2;选择左右子树深度的最大值,将其加一(该结点本身深度)即为该结点子树的深度。
伪码算法:
intBiTreeDepth(BiTreeT)
{//后序遍历求树T所指二叉树的深度
inthl=0,hr=0;
if(!
T)
return0;
else
{
hl=BiTreeDepth(T->lchild);//求左子树的深度
hr=BiTreeDepth(T->rchild);//求右子树的深度
if(hl>=hr)returnhl+1;
elsereturnhr+1;//子树深度的最大值再加上本身所在结点的深度记为子树的深度
}
}
5)求二叉树的宽度
算法描述:
定义一个数组,来存放每层的结点数,max来存放到这层为止时的最大结点数;
计算每一层的结点数将其赋值给对应的数组元素;每层计算完后,用max与本层结点数比较,若max小,则将本层结点数赋值给max;
最后返回max。
伪码算法:
intBiTreeWidth(BiTreeT,inti)
{
intstaticn[M]={0};//存放各层结点数,M为最先预料的最大树的最大深度
intstaticmax=0;//最大宽度
if(T!
=NULL)
{
if(i==1)//若是访问根结点
{
n[i]++;//第一层加1
i++;//到第二层
if(T->lchild)
n[i]++;//若有左孩子则该层加1
if(T->rchild)
n[i]++;//若有右孩子则该层加1
}
else
{//访问子树的结点
i++;//向下一层
if(T->lchild)
n[i]++;
if(T->rchild)
n[i]++;
}
if(max max=n[i];//取出目前为止,各层结点数的最大值 BiTreeWidth(T->lchild,i);//遍历左子树 BiTreeWidth(T->rchild,i--);//网上退一层后遍历右子树 } returnmax; } 6)删除二叉树的某个结点 算法描述: (1)先序遍历找到该结点; (2)若此结点为空,不必释放;若不为空,则先释放其左右子树的所有结点的空间,再赋为空;(3)再释放根结点的空间,并将这个结点的父节点指向该结点的指针域赋为空。 伪码算法: BiTreeDeleteBiTree(BiTreet) {//释放该结点空间的函数 if(t! =NULL) { t->lchild=DeleteBiTree(t->lchild);//释放其左子树的空间 t->rchild=DeleteBiTree(t->rchild);//释放右子树的空间 free(t);//释放根结点的空间 t=NULL;//将其值赋为空 } returnt; } BiTreeDeleteXTree(BiTreet,TElemTypex) {//先序查找到需删结点位置 if(t! =NULL) { if(t->data==x){//判断是否为指定结点 DeleteBiTree(t);//释放树的空间 t=NULL; } else { t->lchild=DeleteXTree(t->lchild,x); t->rchild=DeleteXTree(t->rchild,x); } } returnt; } 7)复制二叉树 算法描述: 先复制其左子树;再复制其右子树;生成一个新结点,将根复制。 伪码算法: BiTreeGetTreeNode(TElemTypeitem,BiTreelptr,BiTreerptr) {//生成一个其元素值为item,左指针为rattrap,右指针为rptr的结点 BiTreet; t=(BiTree)malloc(sizeof(BiTNode)); t->data=item; t->lchild=lptr; t->rchild=rptr; returnt; } BiTreeCopyTree(BiTreeT) {//已知二叉树的根指针T,返回它的复制品的根指针 BiTreenewlptr,newrptr,newnode; if(! T) returnNULL;//复制一颗空树 if(T->lchild) newlptr=CopyTree(T->lchild);//复制(遍历)其左子树 else newlptr=NULL; if(T->rchild) newrptr=CopyTree(T->rchild);//复制(遍历)其右子树 else newrptr=NULL; newnode=GetTreeNode(T->data,newlptr,newrptr);//生成根结点 returnnewnode; } 4、广义表形式打印二叉树 算法分析: 为了让打印的二叉树更形象,更清楚的反映二叉树各结点的关系,采用广义表形式打印则可以满足要求。 算法描述: 打印根结点,若其还有孩子,则输出“(”;若有左孩子,则打印左子树;若还有右子树,则先打印“,”区分左右孩子,再打印右子树,并输出“)”表示输除完毕。 伪码算法: voidprint(BiTreeT) {//广义表形式打印二叉树 if(T! =NULL) { printf("%c",T->data); if(T->lchild! =NULL||T->rchild! =NULL)//该结点还有孩子 { printf("("); print(T->lchild);//广义表打印左子树 if(T->rchild! =NULL) printf(","); print(T->rchild);//广义表打印右子树 printf(")"); } } } 5、二叉树的常见运算 1)求二叉树的叶子数 算法描述: 若该结点无孩子,则表示为叶子,计数器加一;若该结点右孩子,求其左子树的的叶子;再求其右子树的叶子。 伪码算法: intLeafNum(BiTreeT) {//统计叶子数 intstaticn=0;//定义一个静态局部变量作为计数器 if(T! =NULL) { if(T->lchild==NULL&&T->rchild==NULL)//结点无孩子 n++;//计数器加一 LeafNum(T->lchild);//求左子树的叶子数 LeafNum(T->rchild);//求右子树的叶子数 } returnn; } 2)求不同的度的结点数 算法描述: 定义一个静态局部数组变量来统计不同度的结点数;若该结点既有左孩子又有右孩子则度为2的计数器加一,若该节点只有左孩子或只有右孩子,则度为1的结点数的计数器加一;在对其左右子树进行相同操作。 伪码算法: int*JudgeCBT(BiTreeT) {//统计不同度的结点数 intstaticd[3]={0};//d[1]作为度为1的计数器,d[2]作为度为2的计数器 if(T! =NULL) { if(T->lchild! =NULL&&T->rchild! =NULL)//左右孩子都有 d[2]++; else {//只有一个孩子 if(T->lchild! =NULL&&T->rchild==NULL) d[1]++; else if(T->lchild==NULL&&T->rchild! =NULL) d[1]++; } JudgeCBT(T->lchild);//统计左子树 JudgeCBT(T->rchild);//统计右孩子 } returnd;//返回数组的指针 } 3)求非空子孙结点数 算法描述: 定义一个静态局部变量,作为计数器;若该节点的左孩子非空则加一,右孩子非空亦加一;统计左子树;统计右子树。 伪码算法: intBiTreeDescendant(BiTreeT) {//统计非空子孙结点数 intstaticn=0;//初始化计数器 if(T! =NULL) { if(T->lchild) n++; if(T->rchild) n++; BiTreeDescendant(T->lchild);//统计左子树 BiTreeDescendant(T->rchild);//统计右子树 } returnn; } 4)查找二叉树的某个位置的结点(先序序列) 算法描述: 定义一个静态局部变量,用作记录已查结点的个数;从根节点开始,如果相查找的位置理由元素,则打印该元素,如果想查的位置超过二叉树本身的元素数则返回出错。 伪码算法: intPreOderKNode(BiTreeT,intk,TElemType*e) {//T为二叉树,k为带查找的结点位序;e为指针带值返回查找到的数。 intstaticcount=0;//作为计数器,记录已查找的结点数 if(T==NULL) return0; count++;//访问结点,对已访问的结点进行计数 if(count==k)//判断该结点是否是待查找的结点 { e=&T->data; printf("存在你想查找位置的元素,先序序列中第%d个元素是%c\n",k,*e); return1;//查找成功 } else if(count>k)//计数器count已经超出k,则直接返回0,查找不成功 return0; else {//在左或右子树中查找 if(PreOderKNode(T->lchild,k,e)==0) returnPreOderKNode(T->rchild,k,e); return1; } } 6、主程序的伪码算法: voidmain() { BiTreeT=NULL,T_1=NULL,T_2=NULL; inti=1,*n=0,*kd,k=0; char
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 课程设计 二叉 操作