收藏 分享(赏)

重庆大学数据结构第六章 树和二叉树.ppt

上传人:gnk289057 文档编号:6496338 上传时间:2019-04-14 格式:PPT 页数:44 大小:285.50KB
下载 相关 举报
重庆大学数据结构第六章  树和二叉树.ppt_第1页
第1页 / 共44页
重庆大学数据结构第六章  树和二叉树.ppt_第2页
第2页 / 共44页
重庆大学数据结构第六章  树和二叉树.ppt_第3页
第3页 / 共44页
重庆大学数据结构第六章  树和二叉树.ppt_第4页
第4页 / 共44页
重庆大学数据结构第六章  树和二叉树.ppt_第5页
第5页 / 共44页
点击查看更多>>
资源描述

1、第六章 树和二叉树,6.1 树的类型定义 树的抽象数据类型的定义如下: ADT Tree 数据对象:D是具有相同特性的数据元素的集合。 数据关系: 若 D 为空集,则称为空树; 若 D 中仅含一个数据元素,则关系R为空集; 否则 R=H, (1) 在D中存在唯一的称为根的数据元素 root,它在关系H下无前驱; (2) 当n1时,其余数据元素可分为 m(m0) 个互不相交的(非空)有限集 T1,T2,Tm, 其中每一个子集本身又是一棵符合本定义的树,称为根 root 的子树,每一棵子树的根 xi 都是根 root 的后继,即 H,i=1,2,m。基本操作: p.119 ADT Tree,就结构

2、中数据元素之间存在的关系可将树和线性结构作如下对照: 线性结构 树结构1.存在唯一的没有前驱的“首元素” 1.存在唯一的没有前驱的“根结点” 2.存在唯一的没有后继的“尾元素“ 2.存在多个没有后继的“叶子“ 3.其余元素均存在唯一的“前驱元素“ 3.其余结点均存在唯一的”前驱(双亲)点”和唯一的“后继元素” 和多个“后继(孩子)结点“,二叉树的抽象数据类型定义如下: ADT BinaryTree 数据对象:D 是具有相同特性的数据元素的集合。 数据关系: 若 D 为空集,称 BinaryTree 为空二叉树; 否则 关系 R=H: (1) 在 D 中存在唯一的称为根的数据元素 root,它在

3、关系 H 下无前驱; (2) D 中其余元素必可分为两个互不相交的子集 L 和 R,每一个子集都是一棵符合本定义的二叉树,并分别为 root 的左子树和右子树。如果左子树 L 不空,则必存在一个根结点XL ,它是 root 的“左后继”(H),如果右子树 R 不空,则必存在一个根结点 XR 为 root 的“右后继”(H)。基本操作: p.121 ADT BinaryTree,6.2.2 二叉树的几个特性 一、在二叉树的第 i 层上至多有 2i-1 个结点(i1)。 证明: 归纳基础:i=1 时,只有一个根结点。显然 2i-1=20=1 是对的。 归纳假设:设对所有的j(1ji),命题成立,即

4、第j层上至多有 2j-1 个结点。 归纳证明:由归纳假设“第 i-1 层上至多有 2i-2 个结点“,又二叉树的每个结点的度至多为2,则第 i 层上的最大结点数为第 i-1 层上最大结点数的2倍,即 22i-2=2i-1。 证毕。,二、深度为k的二叉树中至多含有 2k-1 个结点,(k1)。证明: 由特性一可推出,深度为k的二叉树上最大结点数为,三、对任何一棵二叉树 T,如果其终端结点数为n0 ,度为2的结点数为n2 ,则 n0 = n2 + 1 证明: 由于二叉树中只有三种结点,假设n1为二叉树 T 中度为1的结点数,则二叉树中结点总数为 n = n0 + n1 + n2 再看二叉树中的分支

5、数目。除了根结点外,其余结点都有一个分支进入,假设 B 为分支数,则 n=B+1。从另一角度看,这些分支是由度为1或2的结点射出的,所以又有 B = n1 + 2n2 即 n = n1 + 2n2 + 1 综合以上和两个等式便可得到 n0 = n2 + 1,有两种特殊形态的二叉树。 若二叉树中所有的分支结点的度数都为2,且叶子结点都在同一层次上,则称这类二叉树为满二叉树。 假如一棵包含 n 个结点的二叉树中每个结点都可以和满二叉树中编号为1至 n 的结点一、一对应,则称这类二叉树为完全二叉树。,如图所示为含10个结点的完全二叉树,树上各个结点 恰好和满二叉树中编号为1至10的结点一一对应。 显

6、然一棵深度为h的完全二叉树中,前h-1层中的结点都是“满“的,且第 h 层的结点都集中在左边。显然,满二叉树本身也是完全二叉树。,四、具有n个结点的完全二叉树的深度为 log2n +1。 证明: 假设该完全二叉树的深度为 k,则根据特性二和完全二叉树的定义 2k-1 -1 n 2k -1 或 2k-1 n 2k 对后者取对数便得:k-1 log2n k 因为k是整数,所以:k= log2n +1 。,五、如果对一棵有 n 个结点的完全二叉树(其深度为 log2n +1)的结点按层序(从第1层到第 log2n +1 层,每层从左到右)从1起开始编号,则对任一编号为 i 的结点(1in),有 (1

7、) 如果 i=1,则编号为 i 的结点是二叉树的根,无双亲;如果 i1,则其双亲结点 parent(i) 的编号是 i/2 。 (2) 如果 2in,则编号为 i 的结点无左孩子(编号为 i 的结点为叶子结点);否则其左孩子结点 lChild(i) 的编号是 2i 。 (3) 如果 2i+1n,则编号为 i 的结点无右孩子;否则其右孩子结点 rChild(i) 的编号是结点 2i+1。,6.3.1 顺序存储结构 用一组地址连续的存储单元存储二叉树中的数据元素。 二叉树的顺序存储结构的定义如下: const MAXSIZE = 100; / 暂定二叉树中结点数的最大值为100 typedef s

8、truct ElemType *data; / 存储空间基址(初始化时分配空间) int nodeNum; / 二叉树中结点数 SqBiTree; / 二叉树的顺序存储结构为了能在存储结构中反映出结点之间的逻辑关系,必须将二叉树中结点依照一定规律安排在这组存储单元中。,6.3.2 链式存储结构 一、二叉链表 二叉树的二叉链表存储表示 typedef struct BiTNode ElemType data; struct BiTNode *Lchild, *Rchild; / 左、右孩子指针 *BiTree;,二、 三叉链表 二叉树的三叉链表存储表示 typedef struct TriTNo

9、de ElemType data; struct BiTNode *Lchild, *Rchild; / 左、右孩子指针 struct BiTNode *parent;/ 双亲指针 *TriTree;,6.4 二叉树的遍历 “遍历”的含义是对结构中的每个数据元素都访问一次且仅仅访问一次。演示 先序遍历二叉树演示若二叉树为空,则空操作; 否则 (1) 访问根结点; (2) 先序遍历左子树; (3) 先序遍历右子树。 中序遍历二叉树演示若二叉树为空,则空操作; 否则 (1) 中序遍历左子树; (2) 访问根结点; (3) 中序遍历右子树。 后序遍历二叉树演示若二叉树为空,则空操作; 否则 (1)

10、后序遍历左子树; (2) 后序遍历右子树; (3) 访问根结点。,void Preorder (BiTree T,void(*visit)( BiTree ) / 先序遍历以T为根指针的二叉树 if (T) / T=NULL时,二叉树为空树,不做任何操作 visit(T); / 通过函数指针 *visit 访问根结点 Preorder(T-Lchild, visit); / 先序遍历左子树 Preorder(T-Rchild, visit); / 先序遍历右子树 / if ,一、统计二叉树中叶子结点的个数 int CountLeaf (BiTree T) if ( !T ) return(0)

11、;else if (!T-Lchild) ,二、求二叉树的深度演示算法1 void BiTreeDepth(BiTree T, int level, int / if / BiTreeDepth,算法2 int BiTreeDepth(BiTree T) / T指向二叉树的根if (!T) return(0);else depth1=BiTreeDepth(T-Lchild); depth2=BiTreeDepth(T-Rchild); if (depth1depth2)depth=depth1+1;elsedepth=depth2+1;return(depth);/ BiTreeDepth,

12、三、建立二叉树的存储结构-二叉链表演示void CreateBiTree(BiTree / 递归建(遍历)右子树 / else / CreateBiTree,6.5 线索二叉树 6.5.1 二叉树的线索链表 如何保存在遍历过程中得到的前驱和后继信息? 办法一:在结点中增加两个指针域分别指向该结点的前驱和后继,但如此做将使存储结构的存储密度大大降低。 办法二:一个含 n 个结点的二叉链表中有 n+1 个链域的值为“NULL“,可以利用这些空的指针域存放指向前驱和后继的信息,由此得到的二叉树的存储结构称作“线索链表“,其中指向前驱或后继的指针称作“线索“。,二叉树的二叉线索链表存储表示 : typ

13、edef enum PointerType Link=0, Thread=1 ; / 定义指针类型,以 Link 表示指针,Thread 表示线索 typedef struct BiThrNode ElemType data; struct BiThrNode *Lchild, *Rchild; / 左右指针 PointerType LTag, RTag; / 左右指针类型标志 *BiThrTree;,在线索链表中添加了一个“头结点“,头结点的左指针指向二叉树的根结点,其右线索指向中序序列中的最后一个结点,中序序列中的第一个结点的左线索和中序序列中的最后一个结点的右线索均指向头结点,6.5.2

14、 以中序线索链表为存储结构的中序遍历 如何在中序线索链表上进行遍历,关键问题有二:一. 如何找到访问的第一个结点?中序遍历访问的第一个结点必定是 “其左子树为空”的结点。即从根结点出发,顺指针 Lchild 找到其左子树直至某个结点的指针 Lchild 为“线索”止。二. 如何找到每个结点在中序序列中的后继?若结点没有右子树,即结点的右指针类型标志 Rtag 为“Thread”,则指针 Rchild 所指即为它的后继;若结点有右子树,则它的后继应该是其右子树中访问的第一个结点,即从它的右子树根出发,顺指针 Lcuild 直至没有左子树的结点为止,该结点即为它的后继。,算法6.9演示void I

15、nOrderTraverse_Thr(BiThrTree T void (*Visit)(ElemType e) / T 指向中序线索链表中的头结点,头结点的左指针 Lchild / 指向二叉树的根结点,头结点的右线索 Rchild 指向中序遍历 / 访问的最后一个结点。本算法对此二叉树进行中序遍历,对 / 树中每个数据元素调用函数 Visit 进行访问操作 p = T-Lchild; / p 指向二叉树的根结点 while (p!= T) / 空树或遍历结束时, while (p-LTag=Link) p = p-Lchild; Visit(p-data); / 访问其左子树为空的结点 wh

16、ile (p-RTag=Thread / p 指向其右子树根 / while / InOrderTraverse_Thr,6.5.3 线索链表的生成 算法6.1void InOrderThreading(BiThrTree / 建非空树的头结点的“右线索“ / else / InOrderThreading,算法6.11演示void InThreading(BiThrTree p,BiThrTree / 对右子树进行线索化 / if / InThreading,6.6 树和森林的存储表示 6.6.1 树的双亲表示法 树的双亲链表存储表示 const MAX_TREE_SIZE = 100; t

17、ypedef struct / 结点结构 ElemType data; int parent; / 双亲位置域 PTNode; typedef struct / 树结构 PTNode nodesMAX_TREE_SIZE; int r, n; / 根的位置和结点数 PTree;,6.6.2 树的孩子表示法 树的孩子链表存储表示 typedef struct CTNode / 孩子结点 int child; struct CTNode *next; *ChildPtr; typedef struct ElemType data; / 结点的数据元素 ChildPtr firstchild; /

18、孩子链表头指针 CTBox; typedef struct CTBox nodesMAX_TREE_SIZE; int n, r; / 结点数和根结点的位置 CTree;,6.6.3 树和森林的孩子兄弟表示法 树和森林的二叉链表(孩子-兄弟)存储表示 typedef struct CSNode ElemType data; struct CSNode *firstchild, *nextsibling; CSNode, *CSTree;,树的孩子-兄弟链表,森林的孩子-兄弟链表,6.6.4 森林和二叉树的转换,6.7.1 树的遍历 和二叉树的遍历类似,树的遍历问题亦为,从根结点出发,对树中各个

19、结点进行一次且仅进行一次访问。 由对根的访问时机不同可得下列两个算法: 一、先根(次序)遍历树演示若树不空,则先访问根结点,然后依次从左到右先根遍历根的各棵子树; 二、后根(次序)遍历树演示若树不空,则先依次从左到右后根遍历根的各棵子树,然后访问根结点;,6.7.2 森林的遍历 一、先序遍历森林 演示 若森林不空,则可依下列次序进行遍历 (1) 访问森林中第一棵树的根结点; (2) 先序遍历第一棵树中的子树森林; (3) 先序遍历除去第一棵树之后剩余的树构成的森林。 二、中序遍历森林演示 若森林不空,则可依下列次序进行遍历: (1) 中序遍历第一棵树中的子树森林; (2) 访问森林中第一棵树的

20、根结点; (3) 中序遍历除去第一棵树之后剩余的树构成的森林。 树的先根遍历 二叉树的先序遍历树的后根遍历 二叉树的中序遍历森林的先序遍历 二叉树的先序遍历 森林的中序遍历 二叉树的中序遍历。,6.8 最优树和赫夫曼编码 6.8.1 最优树的定义 最优树,又称赫夫曼树(Huffman Tree)是一类带权路径长度最短的树。 由从树的根结点到其余结点的分支构成一条路径,路径上的分支数目称路径长度。树的路径长度指的是从树根到树中其余每个结点的路径长度之和。 结点的带权路径长度则定义为从树根到该结点之间的路径长度与该结点上所带权值的乘积。假设树上有 n 个叶子结点,且每个叶子结点上带有权值为 wk(

21、k=1,2,n),则树的带权路径长度定义为树中所有叶子结点的带权路径长度之和,通常记作其中 I k 为带权 wk 的叶子结点的路径长度。,假设有 n 个权值 w1, w2 , wn ,试构造一棵有n 个叶子结点的二叉树,每个叶子结点带权为 wi 。显然,这样的二叉树可以构造出多棵,其中必存在一棵带权路径长度 WPL 取最小的二叉树,称该二叉树为最优二叉树。 例如,下图中的四棵二叉树,都有5个叶子结点且带相同权值5、6、2、9、7,它们的带权路径长度分别为 WPL = 7 x 3 + 9 x 3 + 5 x 2 + 6 x 2 + 2 x 2 = 74 (左上图) WPL = 2 x 1 + 6

22、 x 3 + 7 x 4 + 9 x 4 + 5 x 2 = 94 (右上图) WPL = 6 x 2 + 7 x 2 + 5 x 3 + 2 x 3 + 9 x 2 = 65 (左下图) WPL = 2 x 1 + 5 x 3 + 7 x 3 + 9 x 3 + 6 x 3 = 83 (右下图),6.8.2 最优树的构造方法 赫夫曼最早给出了一个构造最优树的带有一般规律的算法,俗称赫夫曼算法。现以最优二叉树为例叙述如下: (1) 根据给定的 n 个权值w1 , w2, wn,构成 n 棵二叉树的集合 F=T1 , T2 , Tn ,其中每棵二叉树 Ti 中只有一个带权为 wi的根结点,其左右

23、子树均空。 (2) 在 F 中选取两棵根结点的权值最小的树作为左右子树,构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。 (3) 在 F 中删除这两棵树,同时将新得到的二叉树加入 F 中。 重复(2)和(3),直到 F 只含一棵树为止。这棵树便是所求的赫夫曼树。 演示,6.8.3 最优前缀编码 通常有两类二进制编码: 一类为等长编码,这类编码的二进制串的长度取决于电文中不同的字符个数。 另一类是不等长编码,即各个字符的编码长度不等。它的优点是可以使传送电文的字符串的总长度尽可能地短。若对出现次数较多的字符采用尽可能短的编码,则传送电文的总长便可减少。但在实用的

24、不等长编码中,任意一个字符的编码都不能是另一个字符的编码的前缀,这种编码称为前缀编码。 可以利用二叉树来设计二进制的前缀编码: 若以字符出现的次数为权,构造一棵赫夫曼树,由此得到的二进制前缀编码便为“最优前缀编码”(赫夫曼编码)。即以这组编码传送电文可使电文总长最短(对所有其它前缀编码而言)。,假设电文总只有5个字符,且在电文中出现的频率分别为: 5/29,6/29,2/29,9/29,7/29则所构造的最优前缀编码如图所示。,【本章小结】 在这一章讨论了树和二叉树两种数据类型的定义以及它们的实现方法。树是以分支关系定义的层次结构,结构中的数据元素之间存在着“一对多”的关系,因此它为计算机应用

25、中出现的具有层次关系或分支关系的数据,提供了一种自然的表示方法。 二叉树是和树不同的另一种树型结构,它有明确的左子树和右子树,二叉树的几个重要特性是我们应该熟练掌握的。 树和二叉树的遍历算法是实现各种操作的基础,以确保可以访问到结构中的所有数据元素,并使每一个数据元素只被访问一次。遍历的实质是按某种规则将二叉树中的数据元素排列成一个线性序列,二叉树的线索链表便可看成是二叉树的一种“线性“存储结构,在线索链表上可对二叉树进行“线性化“的遍历,即不需要“递归“,而是从“第一个元素“起,逐个访问“后继元素“直至“后继“为“空“止。因此线索链表是通过遍历生成的,即在遍历过程中保存结点之间的“前驱“和“后继“的关系。 在这一章作为应用,介绍了最优树和最优前缀编码的构造方法,最优树是一种“带权路径长度最短“的树,最优前缀编码是最优二叉树的一种应用。,

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

当前位置:首页 > 网络科技 > 数据结构与算法

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报