数据结构教程第5章 树和二叉树.docx

上传人:b****3 文档编号:6666481 上传时间:2023-05-10 格式:DOCX 页数:27 大小:203.53KB
下载 相关 举报
数据结构教程第5章 树和二叉树.docx_第1页
第1页 / 共27页
数据结构教程第5章 树和二叉树.docx_第2页
第2页 / 共27页
数据结构教程第5章 树和二叉树.docx_第3页
第3页 / 共27页
数据结构教程第5章 树和二叉树.docx_第4页
第4页 / 共27页
数据结构教程第5章 树和二叉树.docx_第5页
第5页 / 共27页
数据结构教程第5章 树和二叉树.docx_第6页
第6页 / 共27页
数据结构教程第5章 树和二叉树.docx_第7页
第7页 / 共27页
数据结构教程第5章 树和二叉树.docx_第8页
第8页 / 共27页
数据结构教程第5章 树和二叉树.docx_第9页
第9页 / 共27页
数据结构教程第5章 树和二叉树.docx_第10页
第10页 / 共27页
数据结构教程第5章 树和二叉树.docx_第11页
第11页 / 共27页
数据结构教程第5章 树和二叉树.docx_第12页
第12页 / 共27页
数据结构教程第5章 树和二叉树.docx_第13页
第13页 / 共27页
数据结构教程第5章 树和二叉树.docx_第14页
第14页 / 共27页
数据结构教程第5章 树和二叉树.docx_第15页
第15页 / 共27页
数据结构教程第5章 树和二叉树.docx_第16页
第16页 / 共27页
数据结构教程第5章 树和二叉树.docx_第17页
第17页 / 共27页
数据结构教程第5章 树和二叉树.docx_第18页
第18页 / 共27页
数据结构教程第5章 树和二叉树.docx_第19页
第19页 / 共27页
数据结构教程第5章 树和二叉树.docx_第20页
第20页 / 共27页
亲,该文档总共27页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

数据结构教程第5章 树和二叉树.docx

《数据结构教程第5章 树和二叉树.docx》由会员分享,可在线阅读,更多相关《数据结构教程第5章 树和二叉树.docx(27页珍藏版)》请在冰点文库上搜索。

数据结构教程第5章 树和二叉树.docx

数据结构教程第5章树和二叉树

第5章树和二叉树

本章主要介绍下列内容(教材的第6章)

 1.树的定义和存储结构

 2.二叉树的定义、性质、存储结构

 3.二叉树的遍历、线索算法

 4.树和二叉树的转换

 5.哈夫曼树及其应用

课时分配:

第1、2节两个学时,第3节四个学时,第4、5节各两个学时,上机六个学时

重点、难点:

二叉树的遍历、线索算法、哈夫曼树及其应用

第一节树

 1.树的定义和基本运算

1.1定义

树是一种常用的非线性结构。

我们可以这样定义:

树是n(n≥0)个结点的有限集合。

若n=0,则称为空树;否则,有且仅有一个特定的结点被称为根,当n>1时,其余结点被分成m(m>0)个互不相交的子集T1,T2,...,Tm,每个子集又是一棵树。

由此可以看出,树的定义是递归的。

结点数据元素的内容及其指向其子树根的分支统称为结点。

结点的度结点的分支数。

终端结点(叶子)度为0的结点。

非终端结点度不为0的结点。

结点的层次树中根结点的层次为1,根结点子树的根为第2层,以此类推。

树的度树中所有结点度的最大值。

树的深度树中所有结点层次的最大值。

有序树、无序树如果树中每棵子树从左向右的排列拥有一定的顺序,不得互换,则称为有序树,否则称为无序树。

森林是m(m≥0)棵互不相交的树的集合。

在树结构中,结点之间的关系又可以用家族关系描述,定义如下:

孩子、双亲结点子树的根称为这个结点的孩子,而这个结点又被称为孩子的双亲。

子孙以某结点为根的子树中的所有结点都被称为是该结点的子孙。

祖先从根结点到该结点路径上的所有结点。

兄弟同一个双亲的孩子之间互为兄弟。

堂兄弟双亲在同一层的结点互为堂兄弟。

1.2树的基本运算

常用操作:

(1)构造一个树CreateTree(T)

(2)清空以T为根的树ClearTree(T)

(3)判断树是否为空TreeEmpty(T)

(4)获取给定结点的第i个孩子Child(T,linklist,i)

(5)获取给定结点的双亲Parent(T,linklist)

(6)遍历树Traverse(T)

对树遍历的主要目的是将非线性结构通过遍历过程线性化,即获得一个线性序列。

树的遍历顺序有两种,一种是先序遍历,即先访问根结点,然后再依次用同样的方法访问每棵子树;另一种是后序遍历,即先依……

2.树的存储结构

2.1双亲表示法

树的双亲表示法主要描述的是结点的双亲关系

类型定义:

#defineMAX_TREE_LINKLIST_SIZE100

typedefstruct{

TElemtypeinfo;

intparent;

}ParentLinklist;

typedefstruct{

ParentLinklistelem[MAX_TREE_LINKLIST_SIZE];

intn;//树中当前的结点数目

}ParentTree;

这种存储方法的特点是寻找结点的双亲很容易,但寻找结点的孩子比较困难。

算法实现举例:

intParent(ParentTreeT,intlinklist)

{if(linklist<0||linklist>=T.n)return-2;

elsereturnT.elem[linklist].parent;

}

2.2孩子表示法

孩子表示法主要描述的是结点的孩子关系。

由于每个结点的孩子个数不定,所以利用链式存储结构更加适宜。

举例如下图(a):

在C语言中,这种存储形式定义如下:

#defineMAX_TREE_LINKLIST_SIZE10

typedefstructChildLinklist

{intchild;//该孩子结点在一维数组中的下标值

structChileLinklist*next;//指向下一个孩子结点

}CLinklist;

typedefstruct{

Elemtypeinfo;//结点信息

CLinklist*firstchild;//指向第一个孩子结点的指针

}TLinklist;

typedefstruct

{TLinklistelem[MAX_TREE_LINKLIST_SIZE];

intn,root;//n为树中当前结点的数目,root为根结点在一维数组中的位置

}ChildTree;

这种存储结构的特点是寻找某个结点的孩子比较容易,但寻找双亲比较麻烦,所以,在必要的时候,可以将双亲表示法和孩子表示法结合起来,即将一维数组元素增加一个表示双亲结点的域parent,用来指示结点的双亲在一维数组中的位置。

获取给定结点第i个孩子的操作算法实现:

intChild(ChildTreeT,intlinklist,inti)

{

if(linklist<0||linklist>=T.n)return-2;

p=T.elem[linklist].firstchild;j=1;

while(p&&j!

=i){p=p->next;j++;}   

if(!

p)return-2;elsereturnp->child;

}

2.3孩子兄弟表示法

孩子兄弟表示法也是一种链式存储结构。

它通过描述每个结点的一个孩子和兄弟信息来反映结点之间的层次关系,其结点结构为:

firstchild

elem

nextsibling

其中,firstchild为指向该结点第一个孩子的指针,nextsibling该结点的下一个兄弟的指针,elem是数据元素内容。

举例如下图:

在C语言中,这种存储形式定义如下:

typedefstructCSLinklist{

Elemtypeelem;

structCSLinklist*firstchild,*nextsibling;

}CSLinklist,*CSTree;

voidAllChild(CSTreeT,CSTreep)//输出树中p指针所指结点的所有孩子信息

{

q=p->fisrtchild;

while(q){printf("%c",q->elem);q=q->nextsibling;}

}

第二节二叉树

1.二叉树的定义和基本运算

1.1定义

定义:

二叉树是另一种树形结构。

它与树形结构的区别是:

(1)每个结点最多有两棵子树;

(2)子树有左右之分。

二叉树也可以用递归的形式定义。

即:

二叉树是n(n≥0)个结点的有限集合。

当n=0时,称为空二叉树;当n>0时,有且仅有一个结点为二叉树的根,其余结点被分成两个互不相交的子集,一个作为左子集,另一个作为右子集,每个子集又是一个二叉树。

二叉树的5种形态

1.2二叉树的基本运算(略见教材)

(1)构造一棵二叉树CreateBTree(BT)

(2)清空以BT为根的二叉树ClearBTree(BT)

(3)判断二叉树是否为空BTreeEmpty(BT)

(4)获取给定结点的左孩子和右孩子LeftChild(BT,linklist),

RightChild(BT,linklist)

(5)获取给定结点的双亲Parent(BT,linklist)

(6)遍历二叉树Traverse(BT)

2.二叉树的性质

二叉树具有下列5个重要的性质。

(证明见教材或《离散数学》,此处略)

【性质1】在二叉树的第i层上最多有2i-1个结点(i≥1)。

【性质2】深度为K的二叉树最多有2K-1个结点(K≥1)。

【性质3】对于任意一棵二叉树BT,如果度为0的结点个数为n0,度为2的结点个数为n2,则n0=n2+1。

证明:

假设度为1的结点个数为n1,结点总数为n,B为二叉树中的分支数。

因为在二叉树中,所有结点的度均小于或等于2,所以结点总数为:

n=n0+n1+n2

(1)

再查看一下分支数。

在二叉树中,除根结点之外,每个结点都有一个从上向下的分支指向,所以,总的结点个数n与分支数B之间的关系为:

n=B+1。

又因为在二叉树中,度为1的结点产生1个分支,度为2的结点产生2个分支,所以分支数B可以表示为:

B=n1+2n2。

将此式代入上式,得:

n=n1+2n2+1

(2)

(1)式减去

(2)式,并经过调整后得到:

n0=n2+1。

满二叉树:

如果一个深度为K的二叉树拥有2K-1个结点,则将它称为满二叉树。

完全二叉树:

有一棵深度为h,具有n个结点的二叉树,若将它与一棵同深度的满二叉树中的所有结点按从上到下,从左到右的顺序分别进行编号,且该二叉树中的每个结点分别与满二叉树中编号为1至n的结点位置一一对应,则称这棵二叉树为完全二叉树。

【性质4】具有n个结点的完全二叉树的深度为

+1。

其中,

的结果是不大于log2n的最大整数。

【性质5】见教材。

3.二叉树的存储结构

二叉树也可以采用两种存储方式:

顺序存储结构和链式存储结构。

3.1顺序存储结构

这种存储结构适用于完全二叉树。

其存储形式为:

用一组连续的存储单元按照完全二叉树的每个结点编号的顺序存放结点内容。

下面是一棵二叉树及其相应的存储结构

在C语言中,这种存储形式的类型定义如下所示:

#defineMAX_TREE_LINKLIST_SIZE100

typedefstruct

{Elemtypeelem[MAX_TREE_LINKLIST_SIZE];//根存储在下标为1的数组单元中

intn;//当前完全二叉树的结点个数

}QBTree;

这种存储结构的特点是空间利用率高、寻找孩子和双亲比较容易。

下面我们给出完全二叉树在这种存储形式下的操作算法。

(1)构造一棵完全二叉树

voidCreateBTree(QBTree*BT,Elemtypeelem[],intn)

{

if(n>=MAX_TREE_LINKLIST_SIZE)n=MAX_TREE_LINKLIST_SIZE-1;

for(i=1;i<=n;i++)

BT->elem[i]=elem[i];

BT->n=n;

}

(2)获取给定结点的左孩子

intLeftCHild(QBTreeBT,intlinklist)

{

if(2*linklist>BT.n)return0;

elsereturn2*linklist;

}

RightChild(BT,linklist)与这个操作类似,学生可试着自行完成。

(3)获取给定结点的双亲

intParent(QBTreeBT,intlinklist)

{

if(1<=linklist&&linklist<=BT.n)returni/2;

elsereturn-1;

}

3.2链式存储结构

在顺序存储结构中,利用编号表示元素的位置及元素之间孩子或双亲的关系,因此对于非完全二叉树,需要将空缺的位置用特定的符号填补,若空缺结点较多,势必造成空间利用率的下降。

在这种情况下,就应该考虑使用链式存储结构。

常见的二叉树结点结构如下所示:

其中,Lchild和Rchild是分别指向该结点左孩子和右孩子的指针,elem是数据元素的内容。

在C语言中的类型定义为:

typedefstructBTLinklist{

Elemtypeelem;

structBTLinklist*Lchild,*Rchlid;

}BTLinklist,*BTree;

下面是一棵二叉树及相应的链式存储结构

这种存储结构的特点是寻找孩子结点容易,双亲比较困难。

因此,若需要频繁地寻找双亲,可以给每个结点添加一个指向双亲结点的指针域,其结点结构如下所示。

第三节遍历二叉树和线索二叉树

二叉树是一种非线性的数据结构,在对它进行操作时,总是需要逐一对每个数据元素实施操作,这样就存在一个操作顺序问题,由此提出了二叉树的遍历操作。

所谓遍历二叉树就是按某种顺序访问二叉树中的每个结点一次且仅一次的过程。

这里的访问可以是输出、比较、更新、查看元素内容等等各种操作。

二叉树的遍历方式分为两大类:

一类按根、左子树和右子树三个部分进行访问;另一类按层次访问。

下面我们将分别进行讨论。

1.按根、左子树和右子树三部分进行遍历

遍历二叉树的顺序存在下面6种可能:

TLR(根左右),TRL(根右左)

LTR(左根右),RTL(右根左)

LRT(左右根),RLT(右左根)

其中,TRL、RTL和RLT三种顺序在左右子树之间均是先右子树后左子树,这与人们先左后右的习惯不同,因此,往往不予采用。

余下的三种顺序TLR、LTR和LRT根据根访问的位置不同分别被称为先序遍历、中序遍历和后序遍历。

(1)先序遍历

若二叉树为空,则结束遍历操作;否则

访问根结点;

先序遍历左子树;

先序遍历右子树。

(2)中序遍历若二叉树为空,则结束遍历操作;否则

中序遍历左子树;

访问根结点;

中序遍历右子树。

(3)后序遍历

若二叉树为空,则结束遍历操作;否则

后序遍历左子树;

后序遍历右子树;

访问根结点。

下面是一棵二叉树及其经过三种遍历得到的相应序列

下面我们再给出两种遍历二叉树的方法:

(1)对一棵二叉树中序遍历时,若我们将二叉树严格地按左子树的所有结点位于根结点的左侧,右子树的所有结点位于根右侧的形式绘制,就可以对每个结点做一条垂线,映射到下面的水平线上,由此得到的顺序就是该二叉树的中序遍历序列

(2)任何一棵二叉树都可以将它的外部轮廓用一条线绘制出来,我们将它称为二叉树的包线,这条包线对于理解二叉树的遍历过程很有用。

由此可以看出:

(1)遍历操作实际上是将非线性结构线性化的过程,其结果为线性序列,并根据采用的遍历顺序分别称为先序序列、中序序列或后序序列;

(2)遍历操作是一个递归的过程,因此,这三种遍历操作的算法可以用递归函数实现。

(1)先序遍历递归算法

voidPreOrder(BTreeBT)

{

if(BT){Visit(BT);

PreOrder(BT->Lchild);

PreOrder(BT->Rchild);}

}

(2)中序遍历递归算法

voidInOrder(BTreeBT)

{

if(BT){

InOrder(BT->Lchild);

Visit(BT);

InOrder(BT->Rchild);

}

}

(3)后序遍历递归算法

voidPostOrder(BTreeBT)

{

if(BT){

PostOrder(BT->Lchild);

PostOrder(BT->Rchild);

Visit(BT);

}

}

2.按层次遍历二叉树

实现方法为从上层到下层,每层中从左侧到右侧依次访问每个结点。

下面我们将给出一棵二叉树及其按层次顺序访问其中每个结点的遍历序列。

voidLevelOreder(QBTreeBT)

{

for(i=1;i<=BT.n;i++)

if(BT.elem[i]!

='#')Visite(BT.elem[i]);

}

二叉树用链式存储结构表示时,按层遍历的算法实现

访问过程描述如下:

访问根结点,并将该结点记录下来;

若记录的所有结点都已处理完毕,则结束遍历操作;否则重复下列操作。

取出记录中第一个还没有访问孩子的结点,若它有左孩子,则访问左孩子,并将记录下来;若它有右孩子,则访问右孩子,并记录下来。

在这个算法中,应使用一个队列结构完成这项操作。

所谓记录访问结点就是入队操作;而取出记录的结点就是出队操作。

这样一来,我们的算法就可以描述成下列形式:

(1)访问根结点,并将根结点入队;

(2)当队列不空时,重复下列操作:

从队列退出一个结点;

若其有左孩子,则访问左孩子,并将其左孩子入队

若其有右孩子,则访问右孩子,并将其右孩子入队;

voidLevelOrder(BTree*BT)

{

if(!

BT)exit;

InitQueue(Q);p=BT;//初始化

Visite(p);EnQueue(&Q,p);//访问根结点,并将根结点入队

while(!

QueueEmpty(Q)){//当队非空时重复执行下列操作

DeQueue(&Q,&p);//出队

if(!

p->Lchild){Visite(p->Lchild);EnQueue(&Q,p->Lchild);//处理左孩子

if(!

p->Rchild){Visite(p->Rchild);EnQueue(&Q,p->Rchild);//处理右孩子

}

}

3.二叉树的典型操作算法(线索化根据时间多少按教材略讲)

(1)输入一个二叉树的先序序列,构造这棵二叉树

为了保证唯一地构造出所希望的二叉树,在键入这棵树的先序序列时,需要在所有空二叉树的位置上填补一个特殊的字符,比如,'#'。

在算法中,需要对每个输入的字符进行判断,如果对应的字符是'#',则在相应的位置上构造一棵空二叉树;否则,创建一个新结点。

整个算法结构以先序遍历递归算法为基础,二叉树中结点之间的指针连接是通过指针参数在递归调用返回时完成。

BTreePre_Create_BT()

{

getch(ch);

if(ch=='#')returnNULL;//构造空树

else{BT=(BTree)malloc(sizeof(BTLinklist));//构造新结点

BT->data=ch;

BT->lchild=Pre_Create_BT();//构造左子树

BT->rchild=Pre_Create_BT();//构造右子树

returnBT;

}

}

(2)计算一棵二叉树的叶子结点数目

这个操作可以使用三种遍历顺序中的任何一种,只是需要将访问操作变成判断该结点是否为叶子结点,如果是叶子结点将累加器加1即可。

下面这个算法是利用中序遍历实现的。

voidLeaf(BTreeBT,int*count)

{

if(BT){

Leaf(BT->child,&count);//计算左子树的叶子结点个数

if(BT->lchild==NULL&&BT->rchild==NULL)(*count)++;

Leaf(BT->rchild,&count);//计算右子树的叶子结点个数

}

}

(3)交换二叉树的左右子树

许多操作可以利用三种遍历顺序的任何一种,只是某种遍历顺序实现起来更加方便一些。

而有些操作则不然,它只能使用其中的一种或两种遍历顺序。

将二叉树中所有结点的左右子树进行交换这个操作就属于这类情况。

voidchange_left_right(BTreeBT)

{

if(BT){

change_left_right(BT->lchild);

change_left_right(BT->rchild);

BT->lchild<->BT->rchild;

}

}

(4)求二叉树的高度

这个操作使用后序遍历比较符合人们求解二叉树高度的思维方式。

首先分别求出左右子树的高度,在此基础上得出该棵树的高度,即左右子树较大的高度值加1。

inthight(BTreeBT)

{//h1和h2分别是以BT为根的左右子树的高度

if(BT==NULL)return0;

else{

h1=hight(BT->lchild);

h2=hight(BT->right);

returnmax{h1,h2}+1;

}

}

二叉树建立和遍历的程序补充:

/*binarybtcreat.c递归*/

#include

#include

#include

typedefcharelemtype;

typedefstructbitnode

{elemtypedata;

structbitnode*lchild,*rchild;}bitnode,*bitree;

voidcreat(bitree&t)

{charch;

scanf("%c",&ch);

if(ch=='')t=NULL;

else{/*t=(bitnode*)malloc(sizeof(bitnode));*/

t=newbitnode;

t->data=ch;

creat(t->lchild);

creat(t->rchild);}

}

voidpreorder(bitreet)

{if(t){cout<data;preorder(t->lchild);preorder(t->rchild);}}

voidinorder(bitreet)

{if(t){inorder(t->lchild);cout<data;inorder(t->rchild);}}

voidpostorder(bitreet)

{if(t){postorder(t->lchild);postorder(t->rchild);cout<data;}}

main()

{bitreebt;

creat(bt);

preorder(bt);cout<

inorder(bt);cout<

postorder(bt);cout<

}

/*binarybtcreat1.c*非递归/

#include

#include

#include

typedefcharelemtype;

typedefstructbitnode

{elemtypedata;

structbitnode*lchild,*rchild;}bitnode,*bitree;

typedefstructsnode

{bitreept;

structsnode*next;}snode,*slink;

typedefstructqnode

{bitreept;

structqnode*next;}qnode,*qlink;

voidpush(slink&s,bitreet)

{slinks1;s1=newsnode;s1->pt=t;s1->next=s;s=s1;}

voidpop(slink&s,bitree&t)

{slinks1;if(s){t=s->pt;s1=s;s=s->next;deletes1;}}

voideq(qlink&f,qlink&e,bitreet)

{qlinks1;s1=newqnode;s1->pt=t;s1->next=NULL;

if(e){e->next=s1;e=s1;}elsef=e=s1;}

voiddq(qlink&f,qlink&e,bitree&t)

{qlinks1;

if(f){t=f->pt;s1=f;f=f->next;deletes1;if(!

f)e=f;}}

v

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2