数据结构 第六章 树和二叉树.ppt_第1页
数据结构 第六章 树和二叉树.ppt_第2页
数据结构 第六章 树和二叉树.ppt_第3页
数据结构 第六章 树和二叉树.ppt_第4页
数据结构 第六章 树和二叉树.ppt_第5页
已阅读5页,还剩150页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、1,第 六 章 树 和 二 叉 树,2,1.熟练掌握二叉树的结构特性,了解相应的证明方法。 2. 熟悉二叉树的各种存储结构的特点及适用范围。 3. 遍历二叉树是二叉树各种操作的基础。掌握各种遍历策略的递归算法,灵活运用遍历算法实现二叉树的其它操作。 4.理解二叉树线索化的实质.熟练掌握二叉树的线索化过程以及在中序线索化树上找给定结点的前驱和后继的方法。,本章重点,3,5. 熟悉树的各种存储结构及其特点,掌握树、森林与二叉树的转换方法。 6. 学会编写实现树的各种操作的算法。7. 了解最优树的特性,掌握建立最优树和哈夫曼编码的方法。,4,6.1 树的类型定义,5,数据对象 D:,D是具有相同特性

2、的数据元素的集合。,若D为空集,则称为空树 。 否则: (1) 在D中存在唯一的称为根的数据元素root; (2) 当n1时,其余结点可分为m (m0)个互 不相交的有限集T1, T2, , Tm,其中每一 个子集本身又是一棵符合本定义的树, 称为根root的子树。,数据关系 R:,6,基本操作:,查 找 类,插 入 类,删 除 类,7,Root(T) / 求树T的根结点,查找类:,Value(T, cur_e) / 求cur_e结点的元素值,Parent(T, cur_e) / 求cur_e结点的双亲结点,LeftChild(T, cur_e)/求cur_e结点的最左孩子,RightSibl

3、ing(T, cur_e)/ 求cur_e结点的右兄弟,TreeEmpty(T) / 判定树是否为空树,TreeDepth(T) / 求树的深度,TraverseTree( T, Visit() ) / 遍历,8,InitTree( Value(T, e); Parent(T, e); LeftChild(T, e); RightChild(T, e); LeftSibling(T, e); RightSibling(T, e); BiTreeEmpty(T); BiTreeDepth(T); PreOrderTraverse(T, Visit(); InOrderTraverse(T, Vi

4、sit(); PostOrderTraverse(T, Visit(); LevelOrderTraverse(T, Visit();,查找类:,25,InitBiTree(,插入类:,26,ClearBiTree(,删除类:,27,二叉树的重要 特 性,28,性质1: 在二叉树的第i 层上至多有2i-1 个结点。 (i1),用归纳法证明: 归纳基: 归纳假设: 归纳证明:,i = 1 层时,只有一个根结点: 2i-1 = 20 = 1;,假设对所有的 j,1 j i,命题成立; 当j=i-1时, 命题成立最多有2i-2 个结点,二叉树上每个结点至多有两棵子树, 则第 i 层的结点数 = 2i

5、-2 2 = 2i-1 。,29,性质 2 : 深度为 k 的二叉树上至多含 2k-1个结点(k1)。,证明:,基于上一条性质,深度为 k 的二叉树上的结点数至多为 20+21+ +2k-1 = 2k-1 。 (等比数列求和),30,性质 3 对任何一棵二叉树,若它含有n0 个叶子结点(0度结点)、n2 个度为 2的结点,则必存在关系式:n0 = n2+1。,证明:,设 二叉树上结点总数 n = n0 + n1 + n2 又 二叉树上分支总数 b = n1+2n2 而 b = n-1 = n0 + n1 + n2 - 1 由此, n0 = n2 + 1 。,31,两类特殊的二叉树:,满二叉树:

6、指的是深度为k且含有2k-1个结点的二叉树。,完全二叉树:树中所含的 n 个结点和满二叉树中编号为 1 至 n 的结点一一对应。(编号的规则为,由上到下,从左到右。),1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,a,b,c,d,e,f,g,h,i,j,特点: 1.叶子节点出现在最后2层 2.对于任意结点,若其右分支下的子孙的最大层次为L,则左分支下的子孙的最大层次为L或L+1,32,证明:,设完全二叉树的深度为 k 则根据第二条性质得 2k-1 n 2k 即 k-1 log2 n k 因为 k 只能是整数, 因此, k =log2n + 1 。,性质 4 : 具有

7、n 个结点的完全二叉树的深度为 log2n +1 。,33,性质 5 :,若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:(1) 若 i=1,则该结点是二叉树的根,无双亲,否则,编号为 i/2 的结点为其双亲结点;(2) 若 2in,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;(3) 若 2i+1n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。,34,二、二叉树的链式 存储表示,一、 二叉树的顺序 存储表示,6.3 二叉树的存储结构,35,#define MAX_TREE_SIZE 10

8、0 / 二叉树的最大结点数 typedef TElemType SqBiTreeMAX _TREE_SIZE; / 0号单元存储根结点 SqBiTree bt;,一、 二叉树的顺序存储表示,36,例如:,A,B,C,D,E,F,A B D 0 C 0 E 0 0 0 0 0 0 F,0 1 2 3 4 5 6 7 8 9 10 11 12 13,1,4,0,13,2,6,一般树按完全二叉的方式存储,非常浪费空间!深度为K的单支树,需要2k-1个空间 (k=20 , 1M的空间),37,二、二叉树的链式存储表示,1. 二叉链表,2三叉链表,3双亲链表,4线索链表,38,A,D,E,B,C,F,r

9、oot,lchild data rchild,结点结构:,1. 二叉链表,39,typedef struct BiTNode / 结点结构 TElemType data; struct BiTNode *lchild, *rchild; / 左右孩子指针 BiTNode, *BiTree;,结点结构:,C 语言的类型描述如下:,40,A,D,E,B,C,F,root,2三叉链表,parent lchild data rchild,结点结构:,41,typedef struct TriTNode / 结点结构 struct TriTNode *parent; /双亲指针 TElemType da

10、ta; struct TriTNode *lchild, *rchild; / 左右孩子指针 TriTNode, *TriTree;,parent lchild data rchild,结点结构:,C 语言的类型描述如下:,42,0 1 2 3 4 5 6,data parent,结点结构:,3双亲链表,LRTag,L R R R L,A,B,C,D,E,F,1,4,0,13,2,6,43,typedef struct BPTNode / 结点结构 TElemType data; int parent; / 指向双亲的指针 char LRTag; / 左、右孩子标志域 BPTNode type

11、def struct BPTree / 树结构 BPTNode nodesMAX_TREE_SIZE; int num_node; / 结点数目 int root; / 根结点的位置 BPTree,44,typedef int Status; typedef char TElemType; typedef struct BiTNode TElemType data; struct BiTNode *lchild, *rchild; BiTNode, *BiTree; / 含有头结点,头结点的左儿子指向树的根结点 Status InitBiTree(BiTree T) if (!(T=mallo

12、c(sizeof(BiTNode) return ERROR; T-lchild=NULL; T-rchild=NULL; return OK; ,45,DestroyBiTree(BiTree T) /T为树根结点的指针 if (p-lchild=NULL ,46,BiTree Root (BiTree T) if T-lchild=NULL return ERROR; return T-lchild; Void ClearBiTree (BiTree T) BiTree p; p=T-lchild; if( p-lchild=NULL /其它的基本操作需要用到树的遍历,47,6.4 二叉树

13、的遍历,48,一、问题的提出,二、先左后右的遍历算法,三、算法的递归描述,四、中序遍历算法的非递归描述,五、遍历算法的应用举例,49,顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。(将树线性化),一、问题的提出(寻找某个结点),“访问”的含义可以很广,如:输出结点的信息或判定节点满足某些条件等。,50,“遍历”是任何类型均有的操作, 对线性结构而言,只有一条搜索路 径(因为每个结点均只有一个后继), 故不需要另加讨论。而二叉树是树型结构,每个结点有两个后继,则存在如何遍历,即按什么样的搜索 路径遍历的问题。,51,对“二叉树”而言,可以有三条搜索路径:,1先

14、上后下的按层次遍历; 2先左(子树)后右(子树) 的遍历; 3先右(子树)后左(子树) 的遍历。,52,二、先左后右的遍历算法,先(根)序的遍历算法,中(根)序的遍历算法,后(根)序的遍历算法,53,例:将如下表达式 a+b* ( c-d)-e/ f 存入一个树结构。 叶子结点为操作数 中间结点为运算符,f,/,e,-,d,c,-,a,b,*,+,54,若二叉树为空树,则空操作; 否则 (1)访问根结点; (2)先序遍历左子树; (3)先序遍历右子树。,先(根)序的遍历算法:,顺序:-+a*b-cd/ef 正好是前缀表达式,55,若二叉树为空树,则空操作; 否则 (1)中序遍历左子树; (2)

15、访问根结点; (3)中序遍历右子树。,中(根)序的遍历算法:,顺序:a+b*c-d-e/f 正好是中缀表达式,56,若二叉树为空树,则空操作; 否则 (1)后序遍历左子树; (2)后序遍历右子树; (3)访问根结点。,后(根)序的遍历算法:,顺序:abcd-*+ef/- 正好是后缀表达式,57,三、算法的递归描述,void PreOrderTraverse (BiTree T, void( *visit)(TElemType/遍历右子树 ,A,D,B,T,58,void InOrderTraverse (BiTree T, void( *visit)(TElemType / 遍历右子树 ,59

16、,void PostOrderTraverse (BiTree T, void( *visit)(TElemType / 访问结点 ,60,A,B,1,2,3,总结:无论先序、中序、后序遍历二叉树,遍历时的搜索路线是相同的:从根节点出发,逆时针延二叉树外缘移动,对每个节点均途经三次。 先序遍历:第一次经过节点时访问。 中序遍历:第二次经过节点时访问。 后序遍历:第三次经过节点时访问。,61,四、中序遍历算法的非递归描述,BiTNode *GoFarLeft(BiTree T,Stack *S) / T为树根结点的指针 if (!T ) return NULL; while (T-lchild

17、) Push(S, T); T = T-lchild; return T; /得到树T最左叶子结点的指针,62,void InorderTree(BiTree T, void (*visit) (TelemType /栈空表明遍历结束 / while / InorderTree,63,五、遍历算法的应用举例,1、统计二叉树中叶子结点的个数 (先序遍历),2、求二叉树的深度(后序遍历),3、复制二叉树(后序遍历),4、建立二叉树的存储结构,64,1、统计二叉树中叶子结点的个数,算法基本思想:,先序(或中序或后序)遍历二叉树,在 遍历过程中查找叶子结点,并计数。由此,需在遍历算法中增添一个“计数”

18、 的参数,并将算法中“访问结点”的操 作改为:若是叶子,则计数器增1。,65,void CountLeaf (BiTree T, int* count) / 求叶子节点的个数,T为根节点的指针 if (T) if (!T-lchild) / if / CountLeaf,(方法1),66,int LeafCount_BiTree(Bitree T) /求二叉树中叶子结点的数目 if(!T) return 0; /空树没有叶子 else if(!T-lchild /左子树的叶子数加上右子树的叶子数 /LeafCount_BiTree,方法2,67,2、求二叉树的深度(后序遍历),算法基本思想:,

19、从二叉树深度的定义可知,二叉树 的深度应为其左、右子树深度的最大值 加1。由此,需先分别求得左、右子树 的深度,算法中“访问结点”的操作为 :求得左、右子树深度的最大值,然后 加 1 。,首先分析二叉树的深度和它的左、右子树深度之间的关系。,68,int BiTreeDepth (BiTree T ) / 返回二叉树的深度, T为树根的指针 if ( !T ) depthval = 0; else depthLeft = BiTreeDepth( T-lchild ); depthRight= BiTreeDepth( T-rchild ); depthval = 1 + (depthLeft

20、 depthRight ? depthLeft : depthRight); return depthval; ,69,3、复制二叉树,其基本操作为:生成一个结点。,根元素,T,左子树,右子树,根元素,NEWT,左子树,右子树,(后序遍历),70,BiTNode *GetTreeNode(TElemType item, BiTNode *lptr , BiTNode *rptr ) if (!(T = (BiTNode*) malloc(sizeof(BiTNode) exit(1); T- data = item; T- lchild = lptr; T- rchild = rptr; re

21、turn T; ,生成一个二叉树的结点 (其数据域为item,左指针域为lptr,右指针域为rptr),71,BiTNode *CopyTree(BiTNode *T) if (!T ) return NULL; if (T-lchild ) newlptr = CopyTree(T-lchild);/复制左子树 else newlptr = NULL; if (T-rchild ) newrptr = CopyTree(T-rchild);/复制右子树 else newrptr = NULL; newT = GetTreeNode(T-data, newlptr, newrptr); ret

22、urn newT; / 返回树根的指针,72,A,B,C,D,E,F,G,H,K, D ,C , B, H , K ,G, F ,E ,A,例如:下列二叉树的复制过程如下:,NodeT,73,4、建立二叉树的存储结构,不同的定义方法相应有不同的存储结构的建立算法,74,以字符串的形式 根 左子树 右子 树定义一棵二叉树,例如:,A,B,C,D,以空白字符“ ”表示,A(B( ,C( , ),D( , ),空树,只含一个根结点的二叉树,A,以字符串“A ”表示,以下列字符串表示,75,Status CreateBiTree(BiTree *T) /按前序次序输入结点信息 scanf( / Cre

23、ateBiTree ,无头结点,76,A B C D,A,B,C,D,上页算法执行过程举例如下:,A,T,B,C,D,77,按给定的表达式建相应二叉树, 由先缀表示式建树 例如:已知表达式的先缀表示式 -+ a b c / d e, 由原表达式建树 例如:已知表达式 (a+b)c d/e,78,对应先缀表达式 -+ a b c / d e的二叉树,a,b,c,d,e,-,+,/,特点: 操作数为叶子结点 运算符为分支结点,79,scanf( ,由先缀表示式建树的算法的基本操作:,80,由后缀表示式建树的算法的基本操作:,scanf( ,81,a+b,(a+b)c d/e,a+bc,分析表达式和

24、二叉树的关系:,a,b,+,a,b,c,+,a,b,c,+,(a+b)c,a,b,c,d,e,-,+,/,82,基本操作:,scanf( ,83,对于一个完全二叉树来说,利用先序序列、 中序序列和后序序列可以确定此树。然而,对 于一个一般的二叉树,仅知二叉树的先序序 列“abcdefg” 不能唯一确定一棵二叉树。 如果同时已知二叉树的中序序列“cbdaegf” ,则会如何?,由二叉树的先序和中序序列建树,二叉树的先序序列,二叉树的中序序列,左子树,左子树,右子树,右子树,根,根,84,a b c d e f g,c b d a e g f,例如:,a,a,b,b,c,c,d,d,e,e,f,f

25、,g,g,a,b,c,d,e,f,g,先序序列中序序列,85,例1. 已知树的前序次序为 abdcegf 中序次序为 bdaegcf 则树为?,a,b,d,c,f,e,g,例2. 已知树的后序次序为 dbgefca 中序次序为 bdaegcf 则树为?,我们可以利用前序次序和中序次序、后序次序和中序次序 来确定一棵二叉树。,86,何谓线索二叉树? 线索链表的遍历算法 如何建立线索链表?,6.5线索二叉树,87,一、何谓线索二叉树?,遍历二叉树的结果是, 求得结点的一个线性序列。,A,B,C,D,E,F,G,H,K,例如:,先序序列: A B C D E F G H K,中序序列: B D C

26、A H G K F E,后序序列: D C B H K G F E A,88,指向该线性序列中的“前驱”和 “后继” 的指针,称作“线索”,与其相应的二叉树,称作 “线索二叉树”,包含 “线索” 的存储结构,称作 “线索链表”,A B C D E F G H K, D ,C , B,E ,89,例 求如下二叉树的前序线索树、中序线索树 和后序线索树。,A,B,C,D,A,B,C,D,A,B,C,D,A,B,C,D,前序线索树 中序线索树 后序线索树,abcd bcad cbda,NIL,NIL,NIL,NIL,90,如何保存这种在遍历过程中得到的信息?最简单的方法是在每个结点上增加二个指针域:

27、fwd和bkwd用来指示此结点在遍历中的前驱和后继结点。 这样,使结点的存储密度大大降低。我们知道在n个结点的二叉树中,有n+1个空链域。 (空链域的个数=结点数*2 分支个数) n结点二叉树的空链域=2*n- (n-1)=n+1 我们可以利用这n+1个空链域来存储线索。,91,对线索链表中结点的约定:,在二叉链表的结点中增加两个标志域,并作如下规定:,若该结点的左子树不空, 则Lchild域的指针指向其左子树, 且左标志域的值为“Link(指针)”; 否则,Lchild域的指针指向其“前驱”, 且左标志的值为“Thread(线索) ” 。,92,若该结点的右子树不空,则rchild域的指针指

28、向其右子树,且右标志域的值为 “Link(指针)”;否则,rchild域的指针指向其“后继”,且右标志的值为“Thread(线索) ”。,如此定义的二叉树的存储结构称作 “线索链表”。,93,typedef struct BiThrNod TElemType data; struct BiThrNode *lchild, *rchild; / 左右指针 PointerThr LTag, RTag; / 左右标志 BiThrNode, *BiThrTree;,线索链表的类型描述:,#define Link 0 /指针 #define Thread 1 /线索 typedef enum Link,

29、 Thread PointerThr;,94,要求:已知一个二叉树,我们要会画出它的各种线 索树。,A,B,C,D,E,F,G,H,K,画出它的前序线索树、中序线索树、后序线索树?,95,A,B,C,D,E,F,G,H,K,前序线索树,A B C D E F G H K,NIL,96,A,B,C,D,E,F,G,H,K,后序线索树,D C B H K G F E A,NIL,97,A,B,C,D,E,F,G,H,K,中序线索树,B D C A H G K F E,NIL,NIL,98,二、线索链表的遍历算法:,对于增加二个指针域的结构: for ( p = firstNode(T); p; p

30、 = Succ(p) ) Visit (p);,由于在线索链表中添加了遍历中得到的“前驱”和“后继”的信息,从而简化了遍历的算法。,99,例如:(对于利用空指针域的结构) /中序线索化链表的遍历算法, 中序遍历的第一个结点 ?,在中序线索化链表中结点的后继,左子树上处于“最左下”(没有左子树)的结点。,若无右子树,则为后继线索所指结点;,否则为对其右子树进行中序遍历时访问的第一个结点。,100,void InOrderTraverse_Thr(BiThrTree T, void (*Visit)(TElemType e) p = T-lchild; / p指向根结点 while (p != T

31、) / 空树或遍历结束时,p=T while (p-LTag=Link) p = p-lchild; / 第一个结点 if (!visit(p-data) return ERROR; while(p-RTag=Thread / p进至其右子树根 / InOrderTraverse_Thr,101,在中序遍历过程中修改结点的 左、右指针域,以保存当前访问结 点的“前驱”和“后继”信息。遍历过 程中,附设指针pre, 并始终保持指 针pre指向当前访问的、指针p所指 结点的前驱。,三、如何建立线索链表?,102,/ 已知一棵二叉树,将其线索化 / pre指向 p 的前驱,第一次值为NULL voi

32、d InThreading(BiThrTree p) if (p) / 对以p为根的非空二叉树进行线索化 InThreading(p-lchild); / 左子树线索化 if (!p-lchild) / 建前驱线索 p-LTag = Thread; p-lchild = pre; if (!pre-rchild) / 建后继线索 pre-RTag = Thread; pre-rchild = p; pre = p; / 保持 pre 指向 p 的前驱 InThreading(p-rchild); / 右子树线索化 / if / InThreading,103,A,B,C,D,if (A) /I

33、nThreading(A) ,pre=NULL InThreading(A-lchild); / 左子树线索化 if (!A-lchild) / 建前驱线索 ,pre=C A-LTag = Thread; A-lchild = pre; if (!pre-rchild) / 建后继线索 pre-RTag = Thread; pre-rchild = A; pre = A; / 保持 pre 指向 p 的前驱 InThreading(A-rchild); ,if (B) /InThreading(B) ,pre=NULL InThreading(B-lchild); / 左子树线索化 if (!

34、B-lchild) / 建前驱线索 B-LTag = Thread; B-lchild = pre; if (!pre-rchild) / 建后继线索 pre-RTag = Thread; pre-rchild = B; pre = B; / 保持 pre 指向 p 的前驱 InThreading(B-rchild); ,A,B,C,D,NULL,104,if (C) /InThreading(C) ,pre=B InThreading(C-lchild); / 左子树线索化 if (!C-lchild) / 建前驱线索 C-LTag = Thread; C-lchild = B; if (!

35、B-rchild) / 建后继线索 B-RTag = Thread; B-rchild = C; pre = C; / 保持 pre 指向 p 的前驱 InThreading(C-rchild); /pre=C,if (D) /InThreading(D) ,pre=A InThreading(D-lchild); / 左子树线索化 if (!D-lchild) / 建前驱线索 D-LTag = Thread; D-lchild = A; if (!A-rchild) / 建后继线索 A-RTag = Thread; A-rchild = D; pre = D; / 保持 pre 指向 p 的

36、前驱 InThreading(D-rchild); ,A,B,C,D,A,B,C,D,105,Status InOrderThreading(BiThrTree Thrt, BiThrTree T) / 构建中序线索链表 if (!(Thrt = (BiThrTree)malloc( sizeof( BiThrNode) exit (OVERFLOW); Thrt-LTag = Link; Thrt-RTag =Thread; Thrt-rchild = Thrt; / 添加头结点 return OK; / InOrderThreading, ,106,if (!T) Thrt-lchild

37、= Thrt; else Thrt-lchild = T; pre = Thrt; InThreading(T); /见书上 pre-rchild = Thrt;/处理最后一个结点 pre-RTag = Thread; ,107,6.6 树和森林 的表示方法,108,树的三种存储结构,一、双亲表示法,二、孩子链表表示法,三、树的二叉链表(孩子-兄弟) 存储表示法,109,A,B,C,D,E,F,G,0 A -1 1 B 0 2 C 0 3 D 0 4 E 2 5 F 2 6 G 5,r=0 n=7,data parent,一、双亲表示法:,110,typedef struct PTNode E

38、lem data; int parent; / 双亲位置域 PTNode;,data parent,#define MAX_TREE_SIZE 100,结点结构:,C语言的类型描述:,111,typedef struct PTNode nodes MAX_TREE_SIZE; int r, n; / 根结点的位置和结点个数 PTree;,树结构:,112,A,B,C,D,E,F,G,0 A -1 1 B 0 2 C 0 3 D 0 4 E 2 5 F 2 6 G 5,r=0 n=7,data firstchild,1 2 3,4 5,6,二、孩子链表表示法:,-1 0 0 0 2 2 4,11

39、3,typedef struct CTNode int child; struct CTNode *next; *ChildPtr;,孩子结点结构:,child next,C语言的类型描述:,114,typedef struct Elem data; int parent; ChildPtr firstchild; / 孩子链的头指针 CTBox;,双亲结点结构,data firstchild,115,typedef struct CTBox nodesMAX_TREE_SIZE; int n, r; / 结点数和根结点的位置 CTree;,树结构:,116,A,B,C,D,E,F,G,A B

40、 C E D F G,root,A B C E D F G,三、树的二叉链表 (孩子-兄弟) 存储表示法,117,要求 1. 掌握将一棵树转化为二叉树 2. 将一个森林转化为二叉树,A,B,C,D,E,F,G,H,I,J,K,L,A,B,C,D,E,F,K,L,G,H,I,J,118,B,C,D,E,F,G,H,I,J,K,L,B,C,D,E,F,K,L,G,H,I,J,119,typedef struct CSNode Elem data; struct CSNode *firstchild, *nextsibling; CSNode, *CSTree;,C语言的类型描述:,结点结构:,fi

41、rstchild data nextsibling,120,森林和二叉树的对应关系,设森林 F = ( T1, T2, , Tn ); T1 = (root,t11, t12, , t1m);,二叉树 B =( LBT, Node(root), RBT );,121,。,T1,T2,T3,T4,Tn,T1,T11,T12,T1m,。,T1,T11,T12,T1m,。,T2,T3,Tn,。,122,由森林转换成二叉树的转换规则为:,若 F = ,则 B = ; 否则, 由ROOT( T1 )对应得到 Node(root); 由 (t11, t12, , t1m ) 对应得到 LBT; 由 (T2

42、, T3, Tn ) 对应得到 RBT。,123,由二叉树转换为森林的转换规则为:,若 B = , 则 F = ; 否则, 由Node(root) 对应得到 ROOT( T1 ); 由LBT 对应得到 ( t11, t12, ,t1m); 由RBT 对应得到 (T2, T3, , Tn)。,124,由此,树的各种操作均可对应二叉树的操作来完成。,应当注意的是,和树对应的二叉树,其左、右子树的概念已改变为: 左是孩子,右是兄弟。,125,6.7 树和森林的遍历,126,一、树的遍历,二、森林的遍历,三、树的遍历的应用,127,树的遍历可有三条搜索路径:,按层次遍历:,先根(次序)遍历:,后根(次

43、序)遍历:,若树不空,则先访问根结点,然后依次先根遍历各棵子树。,若树不空,则先依次后根遍历各棵子树,然后访问根结点。,若树不空,则自上而下自左至右访问树中每个结点。,128,A B C D E F G H I J K,先根遍历时顶点的访问次序:,A B E F C D G H I J K,后根遍历时顶点的 访问次序:,E F B C I J K H G D A,层次遍历时顶点的访问次序:,A B C D E F G H I J K,129,B C D E F G H I J K,1森林中第一棵树 的根结点;,2森林中第一棵树 的子树森林;,3森林中其它树构 成的森林。,森林由三部分构成:,1

44、30,1. 先序遍历,森林的遍历,若森林不空,则访问森林中第一棵树的根结点;先序遍历森林中第一棵树的子树森林;先序遍历森林中(除第一棵树之外)其余树构成的森林。,即:依次从左至右对森林中的每一棵树进行先根遍历。,131,中序遍历,若森林不空,则中序遍历森林中第一棵树的子树森林;访问森林中第一棵树的根结点;中序遍历森林中(除第一棵树之外)其余树构成的森林。,即:依次从左至右对森林中的每一棵树进行后根遍历。,132,先根遍历,后根遍历,树,二叉树,森林,先序遍历,先序遍历,中序遍历,中序遍历,树的遍历和二叉树遍历的对应关系 ?,133,设树的存储结构为孩子兄弟链表,typedef struct C

45、SNode Elem data; struct CSNode *firstchild; struct CSNode *nextsibling; CSNode, *CSTree;,134,int TreeDepth(CSTree T) 1 if(!T) return 0; 2 else 3 h1 = TreeDepth( T-firstchild ); 4 h2 = TreeDepth( T-nextsibling); 5 6 7 / TreeDepth,return(max(h1+1, h2);,求树的深度的算法:,135,6.8 哈 夫 曼 树 与 哈 夫 曼 编 码,最优树的定义 如何构造

46、最优树 前缀编码,136,一、最优树的定义,树的路径长度定义为: 树中每个结点的路径长度之 和。,结点的路径长度定义为: 从根结点到该结点的路径上 分支的数目。,137,树的带权路径长度定义为: 树中所有叶子结点的带权路径长度之和WPL(T) = wklk (对所有叶子结点)。,在所有含 n 个叶子结点、并带相同权值的 m 叉树中,必存在一棵其带权路径长度取最小值的树,称为“最优树”。,例如:,138,2,2 4,7,5,4,9,9,WPL(T)= 72+52+23+43+92 =60,WPL(T)= 72+91+53+24+44 =62,5,7,139,2,4,5,7,9,WPL(T)= 9

47、 2+7 2+5 2+ 2 3+4 3 =60,140,根据给定的n个权值w1, w2, , wn, 构造n棵二叉树的集合: F = T1, T2, , Tn, 其中每棵二叉树中均只含一个带权值为wi的根结点,其左、右子树为空树;,二、如何构造最优树,(1),(赫夫曼算法) 以二叉树为例:,141,在 F 中选取其根结点的权值为最小的两棵二叉树,分别作为左、右子树构造一棵新的二叉树,并置这棵新的二叉树根结点的权值为其左、右子树根结点的权值之和;,(2),142,从F中删去这两棵树,同时加入刚生成的新树;,重复(2)和(3)两步,直至F 中只含一棵树为止。,(3),(4),143,9,例如: 已

48、知权值 W= 5, 6, 2, 9, 7 ,5,6,2,7,5,2,7,6,9,7,6,7,13,9,5,2,7,144,6,7,13,9,5,2,7,9,5,2,7,16,6,7,13,29,0,0,0,0,1,1,1,1,00,01,10,110,111,145,赫夫曼编码 假设我们发送的电文为“ABACCD”,它只有 四个字符,只需要2位二进制编码 A - 00 B - 01 C - 10 D - 11 发送电文的序列为 000100101011 在传送电文时,希望总长度尽量短,因此出 现次数多的字符适用短的编码。 A - 0 B - 00 C - 1 D - 11 发送电文的序列为 00001111 可以解释为AAAACCCC,AABDD,BBCCCC.) 我们必须使用前缀编码,146,指的是,任何一个字符的编码都不是同一字符集中另一个字符的编码的前缀。 如A B C D 四个字符的使用率由高到低,编码为 A - 0 B - 10 C - 110 D

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论