




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第六章 树和二叉树,6.1 树的类型定义和基本术语,6.2 二叉树的类型定义及性质,6.3 二叉树的存储结构,6.4 二叉树的遍历,6.5 线索二叉树,6.6 树和森林,6.7 哈夫曼树与哈夫曼编码,6.1 树的类型定义和基本术语,树的定义 定义:树(Tree)是n(n0)个结点的有限集T,其中: 当n1时,有且仅有一个特定的结点,称为树的根(Root), 当n 1时,其余结点可分为m(m0)个互不相交的有限集T1,T2,Tm,其中每一个集合本身又是一棵树,称为根的子树(SubTree)。 特点: 树中各子树是互不相交的集合。,树是一类重要的非线性数据结构,是以分支关系定义的层次结构。,根,子
2、 树,A( ),树根,B(E, F(K, L),C(G),D(H, I, J(M),数据对象 D:,D是具有相同特性的数据元素的集合。,(1) 在D中存在唯一的称为根的数据元素root, (2) 当n1时,其余结点可分为m (m0)个互 不相交的有限集T1, T2, , Tm, 其中每一 个子集本身又是一棵树,称为根root的子树。,数据关系 R:,ADT Tree ,若D为空集,则称为空树; 否则:, ADT Tree,基本操作 P:,基本操作:,查 找 类,插 入 类,删 除 类,Root(T) / 求树的根结点,查找类:,Value(T, cur_e) / 求当前结点的元素值,Paren
3、t(T, cur_e) / 求当前结点的双亲结点,LeftChild(T, cur_e) / 求当前结点的最左孩子,RightSibling(T, cur_e) / 求当前结点的右兄弟,TreeEmpty(T) / 判定树是否为空树,TreeDepth(T) / 求树的深度,TraverseTree( T, Visit() ) / 遍历,树中结点的最大层次,InitTree( Value(T, e); Parent(T, e); LeftChild(T, e); RightChild(T, e); LeftSibling(T, e); RightSibling(T, e); BiTreeEmp
4、ty(T); BiTreeDepth(T); PreOrderTraverse(T, Visit() / 先序遍历; InOrderTraverse(T, Visit() / 中序遍历; PostOrderTraverse(T, Visit() / 后序遍历; LevelOrderTraverse(T, Visit() / 层序遍历;,InitBiTree( InsertChild(T, p, LR, c) / 根据LR为0或1,插入 c为T中p所指结点的左或右子树。p所指结点的原右或左子树成为c的右子树。,ClearBiTree(,二叉树的重要特性,性质 1 : 在二叉树的第 i 层上至多有
5、2i-1 个结点。(i1),用归纳法证明: 归纳基: 归纳假设: 归纳证明:,i = 1 层时,只有一个根结点, 2i-1 = 20 = 1;,假设对所有的 j,1 j i,命题成立;,二叉树上每个结点至多有两棵子树, 则第 i 层的结点数 = 2i-2 2 = 2i-1 。,性质 2 : 深度为 k 的二叉树上至多含 2k-1 个结点. (k1),证明:,基于上一条性质,深度为 k 的二叉树上的结点数至多为 20+21+ +2k-1 = 2k-1,性质 3 : 对任何一棵二叉树,若它含有n0 个叶子结点、n2 个度为 2 的结点,则必存在关系式:n0 = n2+1,证明:,设 二叉树上结点总
6、数 n = n0 + n1 + n2,又 二叉树上分支总数 b = n1 + 2n2,而 b = n - 1,由此, n1 + 2n2 = n0 + n1 + n2 - 1 即 n0 = n2 + 1,两类特殊的二叉树:,满二叉树:指的是深度为k且含有2k-1个结点的二叉树。特点:每一层上的结点数都是最 大结点数.,完全二叉树:树中所含的 n 个结点和满二叉树中编号为 1 至 n 的结点一一对应。特点: (1)叶子结点只可能在层次最底的两层上出现.(2)对任一结点,若其右分支下子孙的最大层次为l,则其左分支下子孙的最大层次必为l 或l+1.,1,2,3,4,5,6,7,8,9,10,11,12
7、,13,14,15,a,b,c,d,e,f,g,h,i,j,证明:,设 完全二叉树的深度为 k,即 k-1 log2 n k,因为 k 只能是整数,因此, k = log2n + 1,性质 4 : 具有 n 个结点的完全二叉树的深度为 log2n +1,性质 5 :,若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点: (1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 i/2 的结点为其双亲结点; (2) 若 2in,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;(3) 若 2i+1n,则该结点无
8、右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。,6.3 二叉树的存储结构,二、 二叉树的链式存储表示,一、 二叉树的顺序存储表示,#define MAX_TREE_SIZE 100 / 二叉树的最大结点数 typedef TElemType SqBiTreeMAX_TREE_SIZE; / 0号单元存储根结点 SqBiTree bt;,一、 二叉树的顺序存储表示,实现:按满二叉树中结点的编号,依次存放二叉 树中的数据元素。 特点:结点间关系蕴含在其存储位置中; 浪费空间,适于存满二叉树和完全二叉树。,二、二叉树的链式存储表示,1. 二叉链表,2三叉链表,3线索链表,typedef
9、struct BiTNode / 结点结构 TElemType data; struct BiTNode *lchild, *rchild; / 左右孩子指针 BiTNode, *BiTree;,C 语言的类型描述如下:,1. 二叉链表,lchild data rchild,结点结构:,A,D,E,B,C,F,root,在有n个结点的二叉链表中,有 ?个空指针域,n+1,typedef struct TriTNode / 结点结构 TElemType data; struct TriTNode *lchild, *rchild, *parent; / 左右孩子指针、双亲指针 TriTNode,
10、 *TriTree;,parent lchild data rchild,结点结构:,C 语言的类型描述如下:,2三叉链表,A,D,E,B,C,F,root,练习:画出下面二叉树的三叉链表。,6.4 二叉树的遍历,一、问题的提出,二、先左后右的遍历算法,三、算法的递归描述,四、中序遍历算法的非递归描述,五、遍历算法的应用举例,遍历:顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。即找一个完整而有规律的走法,得到树中所有结点的一个线性排列。,一、问题的提出,“访问 ”的含义可以很广, 如:输出结点的信息等。,“遍历”是任何类型均有的操作, 对线性结构而言,只有一
11、条搜索路 径(因为每个结点均只有一个后继), 故不需要另加讨论。而二叉树是非 线性结构,,每个结点有两个后继, 则存在如何遍历即按什么样的搜索 路径遍历的问题。,对“二叉树”而言,可以有三条搜索路径:,1先上后下的按层次遍历; 2先左(子树)后右(子树)的遍历; 3先右(子树)后左(子树)的遍历。,二、先左后右的遍历算法,先(根)序的遍历算法,中(根)序的遍历算法,后(根)序的遍历算法,根,左 子 树,右 子 树,若二叉树为空树,则空操作;否则, (1)访问根结点; (2)先序遍历左子树; (3)先序遍历右子树。,先(根)序的遍历算法:,D L R,先序遍历序列:A B D C,先序遍历:,若
12、二叉树为空树,则空操作;否则, (1)中序遍历左子树; (2)访问根结点; (3)中序遍历右子树。,中(根)序的遍历算法:,L D R,中序遍历序列:B D A C,中序遍历:,若二叉树为空树,则空操作;否则, (1)后序遍历左子树; (2)后序遍历右子树; (3)访问根结点。,后(根)序的遍历算法:,L R D,后序遍历序列: D B C A,后序遍历:,分 析:,先序序列:,中序序列:,后序序列:,A B C D E F G H K,B D C A E H G K F,D C B H K G F E A,作业,三、算法的递归描述,void PreOrder (BiTree T, void(
13、 *visit)(TElemType/ 遍历右子树 ,void pre(BiTree t) if (t!=NULL) printf (%dt,t-data); pre(t-lchild); pre(t-rchild); ,返回,返回,返回,返回,A,C,B,D,返回,先序序列:A B D C,递归算法的执行过程,T,如图二叉树表示表达式 : ( a + b ) c d / e 二叉树的先序遍历序列为: + a b c / d e 二叉树的中序遍历序列为: a + b c d / e 二叉树的后序遍历序列为: a b + c d e / ,前缀表达式 中缀表达式 后缀表达式,二叉树与表达式,vo
14、id InOrderTraverse (BiTree T, void (*visit) (TelemType / InOrderTraverse,四、中序遍历算法的非递归描述,非递归算法的执行过程,while ( p|!StackEmpty(S) ) / 找到最左下的结点 if (p) Push(S,p); p=p-lchild; / 根指针进栈,遍历左子树 else /根指针退栈,访问根结点, Pop(S,p); 遍历左子树 if( !visit(p-data) ) return ERROR; p=p-rchild; /else / while,while ( p|!StackEmpty(S
15、) ) / 找到最左下的结点 if (p) Push(S,p); p=p-lchild; / 根指针进栈,遍历左子树 else /根指针退栈,访问根结点, Pop(S,p); 遍历左子树 if( !visit(p-data) ) return ERROR; p=p-rchild; /else / while,p=NULL,p-E p-G,while ( p|!StackEmpty(S) ) / 找到最左下的结点 if (p) Push(S,p); p=p-lchild; / 根指针进栈,遍历左子树 else /根指针退栈,访问根结点, Pop(S,p); 遍历左子树 if( !visit(p-
16、data) ) return ERROR; p=p-rchild; /else / while,P=NULL,P=NULL,while ( p|!StackEmpty(S) ) / 找到最左下的结点 if (p) Push(S,p); p=p-lchild; / 根指针进栈,遍历左子树 else /根指针退栈,访问根结点, Pop(S,p); 遍历左子树 if( !visit(p-data) ) return ERROR; p=p-rchild; /else / while,void PreOrderTraverse (BiTree T, void (*visit) (TelemType /
17、PreOrderTraverse,非递归先序遍历二叉树,五、遍历算法的应用举例,2、统计二叉树中叶子结点的个数,3、求二叉树的深度,4、建立二叉树的存储结构,1、查询二叉树中某个结 点,Status Preorder_Seek (BiTree T, ElemType x, BiTree ,2、统计二叉树中叶子结点的个数,算法基本思想:,先序(或中序或后序)遍历二叉树,在遍历过程中查找叶子结点,并计数。 由此,需在遍历算法中增添一个“计数”的参数,并将算法中“访问结点” 的操作改为:若是叶子,则计数器增1。,void CountLeaf (BiTree T, int / if / CountLe
18、af,算法一,int CountLeaf (BiTree T) if (!T ) return 0; if (!T-lchild /else / CountLeaf,算法二,123456789,3、求二叉树的深度,算法基本思想:,从二叉树深度的定义可知,二叉树的深度应为其左、右子树深度的最大值加1。由此,需先分别求得左、右子树的深度,算法中“访问结点”的操作为:求得左、右子树深度的最大值,然后加 1 。,首先分析二叉树的深度和它的左、右子树深度之间的关系。,int TreeDepth (BiTree T ) / 返回二叉树的深度 if ( !T ) depth = 0; else depthL
19、eft = TreeDepth( T-lchild ); depthRight= TreeDepth( T-rchild ); depth = 1 + (depthLeft depthRight ? depthLeft : depthRight); return depth; ,算法,4、建立二叉树的存储结构,不同的定义方法对应不同的建立存储结构的算法,按先序遍历序列建二叉树 按给定的表达式建二叉树 由先序和中序序列建二叉树,作业,以先序遍历序列的形式 根 左子树 右子树 定义一棵二叉树,例如:,A,B,C,D,以空白字符“ ”表示,A(B( ,C( , ),D( , ),空树,只含一个根结点
20、的二叉树,A,以字符串“A ”表示,以下列字符串表示,算法,Status CreateBiTree(BiTree / CreateBiTree,A B C D,A,B,C,D,上页算法执行过程举例如下:,A,T,B,C,D,A B C D E G F ,练习:按先序遍历序列建立二叉树的二叉链表, 已知先序序列为:,按给定的表达式建相应二叉树, 由前缀表示式建树 例如:已知表达式的前缀表示式 -+ a b c / d e, 由原表达式建树 例如:已知表达式 (a+b)c d/e,对应前缀表达式 -+ a b c / d e的二叉树,a,b,c,d,e,-,+,/,特点: 操作数为叶子结点, 运算
21、符为分支结点,scanf( ,由前缀表示式建树的算法的基本操作:,a+b,(a+b)c d/e,a+bc,分析表达式和二叉树的关系:,a,b,+,a,b,c,+,a,b,c,+,(a+b)c,a,b,c,d,e,-,+,/,算法的基本操作:,scanf( ,PND S,void CrtExptree(BiTree / CrtExptree, ,switch (ch) case ( : Push(S, ch); break; case ) : Pop(S, c); while (c!= ( ) CrtSubtree( t, c); / 建子树并入栈PND Pop(S, c) break; def
22、ult : / switch, ,while(!Gettop(S, c) ,/栈顶元素c优先级高于当前运算符ch,建叶子结点的算法为:,void CrtNode(BiTree ,建子树的算法为:,void CrtSubtree (Bitree ,仅知二叉树的先序序列“abcdefg” 不能唯一确定一棵二叉树,,由二叉树的先序和中序序列建树,如果同时也知二叉树的中序序列“cbdaegf”,则会如何?,二叉树的先序序列,二叉树的中序序列,左子树,左子树,右子树,右子树,根,根,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,g,g,a
23、,b,c,d,e,f,g,先序序列中序序列,作业 :1.分别写出二叉树的先序、中序、后序遍历序列。 2.已知:中序序列:BDCEAFHG 后序序列:DECBHGFA , 画出二叉树。 3.已知:先序序列:ABDFCEG 中序序列:DFBAEGC , 写出后序序列。 4.中序遍历某二叉树的结果为:abc, 问有几种形态的二叉树可得到此遍历结果? 5.写算法统计二叉树中叶子结点的个数。,6.5线索二叉树,何谓线索二叉树? 线索链表的遍历算法 如何建立线索链表?,一、何谓线索二叉树?,遍历二叉树的结果是, 求得结点的一个线性序列。,例如:,先序序列: A B C D E F G H K,中序序列:
24、B D C A H G K F E,后序序列: D C B H K G F E A,若利用二叉链表存储二叉树,则含n个结点的二叉链表中有(n+1) 个空指针域,利用这些指针域指向所在结点在某线性序列中的“前驱”或 “后继” ,以便 于某些操作。这些 指针,称作“线索”。,与其相应的二叉树,称作 “线索二叉树”,包含 “线索” 的存储结构,称作 “线索链表”,A B C D E F G H K (先序序列),对线索链表中结点的约定:,在二叉链表的结点中增加两个标志域,并作如下规定:,若该结点的左子树不空, 则Lchild域的指针指向其左子树, 且左标志域的值为“指针 Link”(0) ; 否则,
25、Lchild域的指针指向其“前驱”, 且左标志域的值为“线索 Thread”(1) 。,若该结点的右子树不空, 则rchild域的指针指向其右子树, 且右标志域的值为 “指针 Link”(0); 否则,rchild域的指针指向其“后继”, 且右标志域的值为“线索 Thread”(1) 。,如此定义的二叉树的存储结构称作“线索链表”。,typedef struct BiThrNode TElemType data; struct BiThrNode *lchild, *rchild; / 左右指针 PointerTag LTag, RTag; / 左右标志 BiThrNode, *BiThrTr
26、ee;,线索链表的类型描述:,typedef enum Link, Thread PointerTag; / Link=0:指针,Thread=1:线索,二、线索链表的遍历算法:,for ( p = firstNode(T); p; p = Next(p) ) Visit (p);,由于在线索链表中添加了遍历中得到的 “前驱”和“后继”的信息,从而简化了遍 历的算法。,例如: 对中序线索化链表的遍历算法。, 中序遍历的第一个结点 ?, 在中序线索化链表中结点*p的后继 ?,根结点左子树上处于“最左下” 的结点。,若*p无右子树,则为*p后继线索所指结点,,否则为对*p右子树进行中序遍历时访问的
27、第一个结点。,for ( p = firstNode(T); p; p = Next(p) ) Visit (p);,作业:写出中序线索二叉树中找指针p所指结点后继的算法。,BiThrNode * next( BiThrNode *p) if (p-rtag) return (p-rchild) / p-rtag为1,表示线索, else /右子树非空 即右子树为空 q= p-rchild; /从*p的右孩子开始查找 while (!q-ltag) q=q-lchild; /当*q不是 return (q); 最左下结点时,继续查找 /else ,void InOrderTraverse (B
28、iThrTree T, void (*Visit)(TElemType e) /基于next(p)函数的中序遍历二叉线索树T的非递归算法 /T指向头结点,头结点的左链lchild指向根结点 p = T-lchild; / p指向根结点 if (p) while (p-LTag=Link) p = p-lchild; do Visit (p-data); p= next (p); while (p!=T) ,有右子树,无右子树,对新子树,void InOrderTraverse_Thr(BiThrTree T, /中序遍历带头结点二叉线索树T的非递归算法void (*Visit)(TElemTy
29、pe e) /T指向头结点,头结点的左链lchild指向根结点 p = T-lchild; / p指向根结点 while (p != T) / 空树或遍历结束时,p=T while (p-LTag=Link) p = p-lchild; /最左下的结点是中序遍历的 Visit(p-data); /第一个结点 while (p-RTag=Thread / p进至其右子树根 / InOrderTraverse_Thr,中序遍历序列: a+b*c-d-e/f,思考:在后序线索二叉树中,如何找结点 *x 的后继结点?,分三种情况:1.若*x是根结点,则其后继为空; 2.若*x是其双亲的右孩子 或*x是
30、其双亲的左孩子但其双亲无右子树, 则后继为其双亲结点; 3.若*x是其双亲的左孩子且其双亲有右子树, 则后继为: 其双亲结点的右子树”后序序列”中的第一个结点; 如:,后序线索化,1. A-空 2. G- E E- B F- C 3. B- H,后序序列:DGEBHFCA,进行后序后继线索化,宜采用三叉链表存储,在中序遍历过程中修改结点的左、右指针域,以保存当前访问结点的“前驱”和“后继”信息。遍历过程中,附设指针pre, 并始终保持指针pre指向当前指针p所指结点的前驱。把中序递归遍历算法中的Visit(p-data)改为设置线索的语句。,三、如何建立(中序)线索链表?,void InThr
31、eading(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,练习:中序线索化二叉树:,中序遍历序列: a+b*c
32、-d-e/f,nil,nil,提高:中序线索树中插入结点。,假设新结点*q是插入到指定结点*p和*p的右子树之间的结点,插入后,*q作为*p的右子树的根结点。,Q,p,q,插入*q后,*q的左子树为空,所以,它是*p右子树中最左下的结点。也就是说,*q是作为*p的中序后继插入的。 !注意:若*p不是中序序列的最后一个结点,则其必有一个中序后继结点*s,插入*q后,*s将变成*q的后继结点。,所以,在中序线索树中插入结点*q时,除需修改*q的两个指针域和*p的右指针域外,还需修改*s的左指针域。,算法片断:s=InOrderNext(p); q-ltag=1(线索); q-lchild=p; q
33、-rtag=p-rtag; q-rchild=p-rchild; p-rtag=0(指针); p-rchild=q; if(s,线索树的优缺点:,优点:线索树由于含有其前驱、后继的 信息,因此在进行各种操作时显 得比较方便。 缺点:就插入和删除结点而言,除需修 改指针外,还须相应的修改线索。,6.6 树和森林,6.6.1 树的三种存储结构,6.6.2 树、森林和二叉树的对应关系,6.6.3 树和森林的遍历,6.6.1 树的三种存储结构,一、双亲表示法(顺序存储),二、孩子链表表示法,三、孩子-兄弟表示法(二叉链表存储),data parent,一、双亲表示法: (顺序存储结构),利用每个结点至
34、多有一个双亲的性质。,对于求双亲或祖先(包括根结点)都十分方便, 但是要求结点的孩子或其他后代,则需遍历整个结构。,typedef struct PTNode TElemType data; int parent; / 双亲位置域 PTNode;,#define MAX_TREE_SIZE 100,结点结构:,C语言的类型描述:,typedef struct PTNode nodesMAX_TREE_SIZE; int r, n; / 根结点的位置和结点个数 PTree;,树结构:,r=0 n=7,二、孩子链表表示法:,树中每个结点可能包含多棵子树, 所以可以采用多叉链表表示。,每个结点包含多
35、个指针域 每个指针指向一棵子树的根结点,每个结点究竟应设多少个指针域?,有以下几种设计方式:,.若以树的度d来设置指针域的个数,显然各个结点是 同构的,便于各种操作。但由于树中很多结点的度都小于d,一棵度为d且含有n个结点的树必有 ? 个空指针域。,n*d-(n-1)=n*(d-1)+1个,显然造成很大的空间浪费。,.若每个结点按其实际的孩子数来设置指针域的个数,并在结点内设置度数域“degree”指出结点所包含的指针的数目。显然各结点是不同构的。虽然节省了空间,但给运算带来了不便。,.以上两种方法在实际应用中都会遇到一些困难。较实用的方法是为树中每个结点建立一个孩子链表,而 n个头指针又组成
36、一个线性表,为了便于查找,可采用顺序存储结构。,-1 0 0 0 2 2 4,data firstchild,parent,与双亲表示法相反,孩子表示法便于实现涉及孩子的操作,却不太适合涉及双亲的操作。,.为了方便各种操作,可以把双亲表示法和孩子表示法结合起来,即带双亲的孩子链表。,typedef struct CTNode int child; struct CTNode *nextchild; *ChildPtr;,孩子结点结构:,孩子链表C语言的类型描述:,typedef struct TElemType data; ChildPtr firstchild; / 孩子链的头指针 CTBo
37、x;,双亲结点结构:,typedef struct CTBox nodesMAX_TREE_SIZE; int n, r; / 结点数和根结点的位置 CTree;,树结构:,typedef struct CSNode TElemType data; struct CSNode *firstchild, *nextsibling; CSNode, *CSTree;,结点结构:,三、孩子-兄弟表示法(二叉链表存储),root,如何找结点*x的第i个孩子?,分别用双亲表示法、孩子链表、孩子兄弟表示法表示图示树。,练习:,孩子兄弟表示法,6.6.2 树、森林和二叉树的对应关系,(一) 树与二叉树之间的
38、转换,二叉树和树都可以用二叉链表作为存储结构。即一棵二叉树或树可唯一的对应一个二叉链表。这样就可以得到二叉树与树之间的对应关系。从物理结构来看,存储它们的二叉链表是相同的,只是解释不同。,树 二叉树,孩子兄弟表示法,二叉链表表示法,内存中的表示,树转换成二叉树的规则,要把一棵树转换为二叉树,只要: 1.在所有兄弟之间加一条连线; 2.对每一个结点,除保留其与第一个孩子之间的 连线外,去掉该结点与其它孩子的连线; 3.为清晰起见,把右孩子顺时针旋转45度。,如:,从转换过程可以看出: 树转换为二叉树后,二叉树根结点的右子树必为空。(因为树根没有兄弟),(二) 森林与二叉树之间的转换,在把森林转换
39、为二叉树时,可以把森林中除第一棵树以外的其它所有树的根结点看作第一棵树根结点的兄弟结点。或者,可以先将森林中的每一棵树变为二叉树,然后将各二叉树的根结点视为兄弟连在一起。,森林转换成二叉树的规则,如:,以上我们给出了:树和森林转化为二叉树的规则, 那么二叉树如何转化为树或森林?看下面例子:,二叉树转换成树或森林的规则,若结点x是其双亲p的左孩子,则把x的右孩子、右孩子的右孩子、都与p用线连起来。然后去掉所有双亲到右孩子的连线。最后作适当的调整即可。,设森林 F = T1, T2, , Tn ; T1 = ( root,t11, t12, , t1m );,二叉树 B = ( root, LB,
40、 RB );,可以给上述转换方法作如下形式定义:,由森林转换成二叉树的转换规则为:,若 F = ,则 B = ;,由 ROOT( T1 ) 对应得到二叉树的根root;,否则,,由 ( t11, t12, , t1m ) 对应得到二叉树的左子树LB;,由 ( T2, T3, , Tn ) 对应得到二叉树的右子树RB。,由二叉树转换为森林的转换规则为:,由LB 对应得到 ( t11, t12, , t1m);,若 B = , 则 F = ;,否则,,由root对应得到 ROOT( T1 );,由RB 对应得到 (T2, T3, , Tn)。,T1,T2,Tn,森林 二叉树,由此,树和森林的各种操
41、作均可与二叉树的操作相对应。,应当注意的是,和树对应的二叉树,其左、右子树的概念已改变为: 左是孩子,右是兄弟。,作业:1.将森林转换为二叉树。,2.将二叉树转换为森林。,一、树的遍历,二、森林的遍历,三、树的遍历的应用,6.6.3 树和森林的遍历,树的遍历 可有三条搜索路径:,按层次遍历:,先根(次序)遍历:,后根(次序)遍历:,若树不空,则先访问根结点,然后依次先根遍历各棵子树。,若树不空,则先依次后根遍历各棵子树,然后访问根结点。,若树不空,则自上而下、自左至右访问树中每个结点。,层次遍历时顶点的访问序列:,先根遍历时顶点的访问序列:,A B E F C D G H I J K,后根遍历
42、时顶点的访问序列:,E F B C I J K H G D A,A B C D E F G H I J K,森林中第一棵树的根结点;,森林中第一棵树的子树森林;,森林中其它树构成的森林。,可以分解成三部分:,森林,若森林不空,则 访问森林中第一棵树的根结点; 先序遍历森林中第一棵树的子树森林; 先序遍历森林中(除第一棵树之外)其 余树构成的森林。,先序遍历:,森林的遍历,即:依次从左至右对森林中的每一棵树进行 先根遍历。,若森林不空,则 中序遍历森林中第一棵树的子树森林; 访问森林中第一棵树的根结点; 中序遍历森林中(除第一棵树之外)其 余树构成的森林。,中序遍历:,即:依次从左至右对森林中的
43、每一棵树进行 后根遍历。,树、森林的遍历和二叉树遍历的对应关系 ?,先根遍历,后根遍历,树,二叉树,森林,先序遍历,先序遍历,中序遍历,中序遍历,先序遍历:,后序遍历:,层次遍历:,A,B,E,F,I,G,C,D,H,J,K,L,N,O,M,E,I,F,G,B,C,J,K,N,O,L,M,H,D,A,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,作业:写出下面树的先根、后根及按层次遍历的遍历序列。,一、求树的深度,二、输出树中所有从根到叶子的路径,三、建树的存储结构,树的遍历的应用,data firstchild,树用孩子链表存储结构存储,一、求树的深度的算法:,typedef
44、struct CTNode int child; struct CTNode *next; *ChildPtr;,孩子结点结构:,C语言的类型描述:,typedef struct Elem data; ChildPtr firstchild; / 孩子链的头指针 CTBox;,双亲结点结构:,typedef struct CTBox nodesMAX_TREE_SIZE; int n, r; / 结点数和根结点的位置 CTree;,树结构:,int TreeDepth( Ctree T ) / T 是树的孩子链表存储结构, / 返回该树的深度 if ( T.n = 0) return 0; e
45、lse return Depth( T, T.r ); / TreeDepth,int Depth( Ctree T, int root ) max = 0; p = T.nodesroot.firstchild; while ( p ) h = Depth( T, p ); if ( h max ) max = h; p = p-next; return max+1; ,二、输出二叉树或树中所有从根到叶子的路径的算法:,例如:对左图所示的树,其输出结果应为:,A B E A B F A C A D G H I A D G H J A D G H K,A B C D E F G H I J K
46、,void AllPath( BiTree T, Stack / if(T) / AllPath,/ 输出二叉树上从根到所有叶子结点的路径,设树的存储结构为孩子兄弟链表,typedef struct CSNode TElemType data; struct CSNode *firstchild, *nextsibling; CSNode, *CSTree;,void OutPath( Bitree T, Stack / while / OutPath 可以推广到森林,/ 输出树中所有从根到叶的路径,三、建树的存储结构的算法:,和二叉树类似,不同的定义相应有不同的算法。,假设以二元组(Pare
47、nt,Child)的形式自上而下、自左而右依次输入树的各边,建立树的孩子-兄弟链表。,A,B,C,D,E,F,G,例如:,对下列所示树的输入序列应为:,(#, A) (A, B) (A, C) (A, D) (C, E) (C, F) (E, G),A,B,C,D,(#, A),(A, B),(A, C),(A, D),(C, E),算法中需要一个队列保存已建好的父结点的指针。,void CreatTree( CSTree / 所建为根结点 else / 非根结点的情况 / for / CreateTree, ,GetHead(Q,s); / 取队列头元素(指针值) while (s-data
48、 != pa ) / 查询双亲结点 DeQueue(Q,s); GetHead(Q,s); if (!(s-firstchild) s-firstchild = p; r = p; / 链接第一个孩子结点 else r-nextsibling = p; r = p; / 链接其它孩子结点,单击此处编辑母版标题样式,单击此处编辑母版文本样式 第二级 第三级 第四级 第五级,165,6.7 哈 夫 曼 树 与 哈 夫 曼 编 码,引例 问题的提出 哈夫曼树系统理论 问题的解决,哈 夫 曼 树,David Huffman,按此程序流程,有80%的数据需进行3次或3次以上比较才能得出结果。,设实际学生
49、成绩的分布规律如下:,(设成绩为 a),引例:百分制成绩转换成五级分制成绩,如何设计程序才能使得比较的总次数最少?,问题:,程序执行的时间,与什么因素有关? 1、CPU的处理速度(硬件因素); 2、程序中执行基本运算的次数,即比较的次数(软件因素)。,树的带权路径长度: 树中所有的叶结点的权值乘上其到根 结点的路径长度之和,记为:,一、哈夫曼树的定义,6,哈夫曼树统称一类带权路径长度最短的树。,假设有n个权值w1, w2, , wn, 构造一棵有 n 个叶子结点的二叉树,每个叶子结点所带权值为wi ,则其中WPL最小的二叉树称为最优二叉树或哈夫曼树。,哈 夫 曼 树 的 定 义,结点间的路径长
50、度 树的路径长度 结点的带权路径长度 树的带权路径长度,(1) 根据给定的 n 个权值 w1, w2, , wn,构造 包含n 棵二叉树的集合F = T1, T2, , Tn,其中每棵二叉树中均只含一个带权值为 wi 的根结点,其左、右子树为空树;,二、如何构造哈夫曼树,(哈夫曼算法),(2) 在 F 中选取其根结点的权值最小的两棵二叉树,分别作为左、右子树构造一棵新的二叉树,并置这棵新的二叉树根结点的权值为其左、右子树根结点的权值之和;,(3) 从F 中删去这两棵树,同时加入刚生成的新树;,(4) 重复 (2) 和 (3) 两步,直至 F 中只含一棵树为止。,9,例:已知权值的集合W= 5,
51、 6, 2, 9, 7 ,构造哈夫曼树。,5,6,2,7,5,2,7,6,9,7,6,7,13,9,9,5,2,7,16,6,7,13,29,哈夫曼树 构造完成!,设实际学生成绩的分布规律如下:,百分制成绩转换成五级分制成绩,问题:如何设计程序才能使得比较的总次数最少?,S = Nx(5%x4+10%x4+15%x3+30%x2+40%x1) = Nx1%x(5x4+10 x4+15x3+30 x2+40 x1),问题的解决,以各个分数段上学生所占的百分比作为权值,构造一棵哈夫曼树:,W= 5, 15, 40, 30, 10 ,(设学生总人数为常数N ),总的比较次数 S为 :,WPL,即 S
52、=N x 1% x WPL,S=N x 1% x WPL,练习:已知权值的集合W= 21,17,6,8,9,8,13 , 构造哈夫曼树。,哈 夫 曼 编 码,引 例 前 缀 编 码 哈夫曼 编 码,电 报,手 机 ( 数字信号 ), 数 据 传 输 主 要 方 式 ,快 速 远 距 离 通 信,数据,二进制电文,原数据,编码,译码,一、引 例,编码方式,编码方式:,等长编码 不等长编码,(一) 等长编码,假设要发送的数据中可能出现的字符只有 4个:A、B、C、D,则可用 2位二进制数进行编码,如图:,若要发送的数据是:“ A B C D ”,编码总长,传送电文时,总是希望电文的总长尽可能 短。
53、,这是基于以下两个原因:,电文越短,则在相同条件下传输越快,效率高 。 电文越短,则在传输中出错的机率越小,准确率高 。,怎样缩短电文的总长度? 可缩短每个字符的编码。如:,不等长编码,(二) 不等长编码,若要发送的数据是: “ A B C D ”,译码出现 “二义性” !,能否在正确的进行编码和译码的前提下,实现电文的总长最短 ?,二、前缀编码,若任何一个字符的编码都不是同一字符集中另一个字符的编码的前缀,则此编码称为前缀编码。,如:等长编码,不等长编码,是前缀编码,非前缀编码,可以证明,采用前缀编码能够保证译码过程中不会出现“二义性”。,可以证明:,二叉树,其四个叶子结点分别表示A、B、C、D 四个字
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年初级经济师之初级金融专业题库综合试卷A卷附答案
- 2025年资料员之资料员专业管理实务通关题库(附带答案)
- 【广州】2025年广东广州市增城区康园工疗站服务中心招聘工作人员3人笔试历年典型考题及考点剖析附带答案详解
- Brand KPIs for milk:Visakha Dairy in India-英文培训课件2025
- Brand KPIs for milk:Dália in Brazil-英文培训课件2025
- 口腔科龋病宣传讲科课件
- 口腔健康课件
- 口腔健康创意美术课件
- 口腔临床知识直播课件
- 潮玩市场IP运营策略报告:2025年行业洞察与竞争格局
- 2025年山西省太原市人大常委会招聘劳务派遣制人员15人历年管理单位笔试遴选500模拟题附带答案详解
- 卖挂靠公司货车的合同(2篇)
- 《材料成型装备及自动化》教学大纲
- 防止口腔治疗中交叉感染
- DB52T+1844-2024+实验室化学废液收集与处理规范
- 2024年人教版二年级语文上册《第1单元1.小蝌蚪找妈妈》课文教学课件
- 土壤和地下水污染生态环境损害鉴定评估案例分析-笔记
- T-XJZJXH 0004-2024 牛奶中糠氨酸的快速测定方法拉曼光谱法
- 全国高中生物奥林匹克竞赛考试大纲
- (新版)拖拉机驾驶证科目一知识考试题库500题(含答案)
- 火锅丸子供货合同范本
评论
0/150
提交评论