第六章树与二叉树.ppt

上传人:sccc 文档编号:5670513 上传时间:2023-08-08 格式:PPT 页数:170 大小:2.08MB
返回 下载 相关 举报
第六章树与二叉树.ppt_第1页
第1页 / 共170页
第六章树与二叉树.ppt_第2页
第2页 / 共170页
第六章树与二叉树.ppt_第3页
第3页 / 共170页
第六章树与二叉树.ppt_第4页
第4页 / 共170页
第六章树与二叉树.ppt_第5页
第5页 / 共170页
点击查看更多>>
资源描述

《第六章树与二叉树.ppt》由会员分享,可在线阅读,更多相关《第六章树与二叉树.ppt(170页珍藏版)》请在三一办公上搜索。

1、第六章树和二叉树,6.1 树的类型定义,6.2 二叉树的类型定义,6.2.3 二叉树的存储结构,6.3 二叉树的遍历,6.3.2 线索二叉树,6.4 树和森林的表示方法,6.4.3 树和森林的遍历,6.6 哈夫曼树与哈夫曼编码,6.1 树的类型定义,树的定义:树是n(n=0)个结点的有限集。在任意一棵非空树中:(1)有且仅有一个特定的称为根的结点;(2)当n1时,其余结点可分为m(m0)个互不相交的有限集T1,T2,Tm,其中每一个集合本身又是一棵树,并称为根的子树。,例如:,树的图示表示法,树的广义表表示法,A,B,D,C,H,I,J,M,F,E,G,K,L,树的嵌套集合表示法,树的凹入表示

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

3、r_e)/求cur_e结点的右兄弟,TreeEmpty(T)/判定树是否为空树,TreeDepth(T)/求树的深度,TraverseTree(T,Visit()/遍历,InitTree(&T)/初始化置空树,插入类:,CreateTree(&T,definition)/按定义构造树,Assign(T,cur_e,value)/给cur_e结点赋值,InsertChild(&T,&p,i,c)/插入c为T中P所指结点的第i棵子树,ClearTree(&T)/将树清空,删除类:,DestroyTree(&T)/销毁树的结构,DeleteChild(&T,&p,i)/删除结点p的第i棵子树,基 本

4、 术 语,结点:,结点的度:,树的度:,叶子结点:,分支结点:,数据元素+若干指向子树的分支,分支的个数(子树的个数),树中所有结点的度的最大值,度为零的结点,度大于零的结点(包含根和中间节点),组织结构树,山东理工大学,叶子,根,子树,赵老根,赵跃进,赵小康,赵改革,赵开放,赵解放,赵抗美,赵卫兵,赵永红,家谱树,(从根到结点的)路径:,孩子结点、双亲结点兄弟结点、堂兄弟祖先结点、子孙结点,结点的层次:,树的深度:,由从根到该结点所经分支和结点构成,A,B,C,D,E,F,G,H,I,J,M,K,L,假设根结点的层次为1,根的孩子为第二层,如果某节点在第L层,则其子树的根在L+1层。,树中叶

5、子结点所在的最大层次,有序树:,子树之间存在确定的次序关系。,无序树:,子树之间不存在确定的次序关系。,任何一棵非空树是一个二元组 Tree=(root,F)其中:root 被称为根结点 F 被称为子树森林,森林:,是m(m0)棵互不相交的树的集合,A,root,B,C,D,E,F,G,H,I,J,M,K,L,F,对比树型结构和线性结构的结构特点,线性结构,树型结构(非线性结构),第一个数据元素(无前驱),根结点(无前驱),最后一个数据元素(无后继),多个叶子结点(无后继),其它数据元素(一个前驱、一个后继),其它数据元素(一个前驱、多个后继),6.2 二叉树的类型定义,二叉树或为空树,或是由

6、一个根结点加上两棵分别称为左子树和右子树的、互不交的二叉树组成。(树的度最大为2),A,B,C,D,E,F,G,H,K,根结点,左子树,右子树,二叉树的五种基本形态:,N,空树,只含根结点,N,N,N,L,R,R,右子树为空树,L,左子树为空树,左右子树均不为空树,二叉树的主要基本操作:,查 找 类,插 入 类,删 除 类,Root(T);Value(T,e);Parent(T,e);LeftChild(T,e);RightChild(T,e);LeftSibling(T,e);RightSibling(T,e);BiTreeEmpty(T);BiTreeDepth(T);PreOrderTr

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

8、数=2i-2 2=2i-1。,性质 2:深度为 k 的二叉树上至多含 2k-1 个结点(k1)。,证明:,基于上一条性质,深度为 k 的二叉树上的结点数至多为 20+21+2k-1=2k-1。(等比数列求和),性质 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。,两类特殊的二叉树:,满二叉树:指的是深度为k且含有2k-1个结点的二叉树。,完全二叉树:树中所含的 n 个结点和满

9、二叉树中编号为 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,性质 4:具有 n 个结点的完全二叉树的深度为 log2n+1。,证明:,设完全二叉树的深度为 k 则根据第二条性质得 2k-1 n 2k 即 k-1 log2 n k 因为 k 只能是整数,因此,k=log2n+1,性质 5:,若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:(1)若 i=1,则该结点是二叉树的根,无双亲,否则,编

10、号为 i/2 的结点为其双亲结点;(2)若 2in,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;(3)若 2i+1n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。,6.2.3 二叉树的存储结构,二、二叉树的链式 存储表示,一、二叉树的顺序 存储表示,#define MAX_TREE_SIZE 100/二叉树的最大结点数typedef TElemType SqBiTreeMAX_ TREE_SIZE;/1号单元存储根结点SqBiTree bt;,一、二叉树的顺序存储表示,例如:,A,B,C,D,E,F,A B D 0 C 0 E 0 0 0 0 0 0 F,

11、1 2 3 4 5 6 7 8 9 10 11 12 13 14,2,5,1,14,3,7,一般树按完全二叉的方式存储,非常浪费空间!深度为K的单支树,需要2k-1个空间(k=20,1M的空间),二、二叉树的链式存储表示,1.二叉链表,2三叉链表,A,D,E,B,C,F,root,1.二叉链表,lchild data rchild,结点结构:,typedef struct BiTNode/结点结构 TElemType data;struct BiTNode*lchild,*rchild;/左右孩子指针 BiTNode,*BiTree;,lchild data rchild,结点结构:,C 语言

12、的类型描述如下:,A,D,E,B,C,F,root,2三叉链表,parent lchild data rchild,结点结构:,typedef struct TriTNode/结点结构 struct TriTNode*parent;/双亲指针 TElemType data;struct TriTNode*lchild,*rchild;/左右孩子指针 TriTNode,*TriTree;,parent lchild data rchild,结点结构:,C 语言的类型描述如下:,6.3二叉树的遍历,顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。,一、问题的提出(

13、寻找某个节点),“访问”的含义可以很广,如:输出结点的信息或判定节点满足某些条件等。,“遍历”是任何类型均有的操作,对线性结构而言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论。而二叉树是非线性结构,,每个结点有两个后继,则存在如何遍历即按什么样的搜索路径遍历的问题。,对“二叉树”而言,可以有三条搜索路径:,1先上后下的按层次遍历;2先左(子树)后右(子树)的遍历;3先右(子树)后左(子树)的遍历。,二、先左后右的遍历算法,先(根)序的遍历算法,中(根)序的遍历算法,后(根)序的遍历算法,若二叉树为空树,则空操作;否则,(1)访问根结点;(2)先序遍历左子树;(3)先序遍历

14、右子树。,先(根)序的遍历算法:,D L R,先序遍历序列:A B D C,先序遍历:,若二叉树为空树,则空操作;否则,(1)中序遍历左子树;(2)访问根结点;(3)中序遍历右子树。,中(根)序的遍历算法:,L D R,中序遍历序列:B D A C,中序遍历:,若二叉树为空树,则空操作;否则,(1)后序遍历左子树;(2)后序遍历右子树;(3)访问根结点。,后(根)序的遍历算法:,L R D,后序遍历序列:D B C A,后序遍历:,三、算法的递归描述,void PreOrderTraverse(BiTree T,void(*visit)(TElemType/遍历右子树,A,D,B,T,A,B,

15、D,void InOrderTraverse(BiTree T,void(*visit)(TElemType/遍历右子树,B,A,D,void PostOrderTraverse(BiTree T,void(*visit)(TElemType/访问结点,B,D,A,可以这样理解:无论先序、中序、后序遍历二叉树,遍历时的搜索路线是相同的:从根节点出发,逆时针沿二叉树外缘移动对每个节点均途径三次。先序遍历:第一次经过节点时访问。中序遍历:第二次经过节点时访问。后序遍历:第三次经过节点时访问。,A,B,1,2,3,先序:A B,中序:A B,后序:B A,1,2,3,root,LDR:a*b-c+d

16、/e,LRD:abc-*de/+,DLR:+*a-bc/de,中序遍历二叉树的非递归算法1Status InOrderTraverse(BiTree T,Status(*Visit)(TElemType e)InitStack(S);Push(S,T);/根指针进栈While(!StadkEmpty(S)while(GetTop(S,p)/InOrderTraverse,中序遍历二叉树的非递归算法2Status InOrderTraverse(BiTree T,Status(*Visit)(TElemType e)InitStack(S);p=T;While(p|!StadkEmpty(S)i

17、f(p)Push(S,p);p=p-lchild;)/根指针进栈,遍历左子树 else/根指针退栈,访问根结点,遍历右子树 Pop(S,p);if(!visit(p-data)return ERROR;p=p-rchild);/endif/endwhile return OK;/InOrderTraverse,四、建立二叉树的存储结构,不同的定义方法相应有不同的存储结构的建立算法,以字符串的形式 根 左子树 右子树定义一棵二叉树,例如:,A,B,C,D,以空白字符“”表示,A B C D,空树,只含一个根结点的二叉树,A,以字符串“A”表示,以字符串表示,Status CreateBiTree

18、(BiTree*T)/按前序次序输入节点信息 scanf(/CreateBiTree,无头节点,A B C D,A,B,C,D,上页算法执行过程举例如下:,A,T,B,C,D,对于一个一般的二叉树,仅知二叉树的先序序列“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,b,c,d,e,f,g,先序序列中序序列,6.3.

19、2线索二叉树,何谓线索二叉树?线索链表的遍历算法 如何建立线索链表?,一、何谓线索二叉树?,遍历二叉树的结果是,求得结点的一个线性序列。,A,B,C,D,E,F,G,H,K,例如:,先序序列:A B C D E F G H K,中序序列:B D C A H G K F E,后序序列:D C B H K G F E A,指向该线性序列中的“前驱”和“后继”的指针,称作“线索”,与其相应的二叉树,称作“线索二叉树”,包含“线索”的存储结构,称作“线索链表”,A B C D E,D,C,B,E,A,如何保存这种在遍历过程中得到的信息?最简单的方法是在每个结点上增加二个指针域:fwd和bkwd用来指示

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

21、针指向其“后继”,且右标志的值为“Thread(线索)”。,如此定义的二叉树的存储结构称作“线索链表”。,线索链表的结点定义:,Lchild Ltag Data Rtag Rchild,0 lchild 域指示结点的左孩子 1 rchild 域指示结点的前驱,Ltag=,0 lchild 域指示结点的右孩子 1 rchild 域指示结点的后继,Rtag=,以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表。,其中指向结点前驱和后继的指针,叫做线索。,加上线索的二叉树称之为线索二叉树。,对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。,typedef struct BiTh

22、rNod TElemType data;struct BiThrNode*lchild,*rchild;/左右指针 PointerTag LTag,RTag;/左右标志 BiThrNode,*BiThrTree;,线索链表的类型描述:,#define Link 0/指针#define Thread 1/线索 typedef enum PointerTag Link,Thread;,定义枚举类型:Enum 枚举变量名 枚举变量的值,二、线索链表的遍历算法:,由于在线索链表中添加了遍历中得到的“前驱”和“后继”的信息,从而简化了遍历的算法。,中序遍历二叉线索树:B C A D,例如:(对于利用空指

23、针域的结构)/中序线索化链表的遍历算法,中序遍历的第一个结点?,在中序线索化链表中结点的后继?,左子树上处于“最左下”(没有左子树)的结点。,若无右子树,则为后继线索所指结点;,否则为对其右子树进行中序遍历时访问的第一个结点。,status InOrderTraverse_Thr(BiThrTree T,void(*Visit)(TElemType e)p=T-lchild;/p指向根结点 while(p!=T)/空树或遍历结束时,p=T while(p-LTag=Link)p=p-lchild;/第一个结点 if(!visit(p-data)return ERROR;while(p-RTag

24、=Thread/p进至其右子树根/while return OK;/InOrderTraverse_Thr,在中序遍历过程中修改结点的左、右指针域,以保存当前访问结点的“前驱”和“后继”信息。遍历过程中,附设指针pre,并始终保持指针pre指向当前访问的、指针p所指结点的前驱。,三、如何建立线索链表?,Status InOrderThreading(BiThrTree Thrt,BiThrTree T)/构建中序线索链表 if(!(Thrt=(BiThrTree)malloc(sizeof(BiThrNode)exit(OVERFLOW);Thrt-LTag=Link;Thrt-RTag=Th

25、read;Thrt-rchild=Thrt;/添加头结点 return OK;/InOrderThreading,if(!T)Thrt-lchild=Thrt;else Thrt-lchild=T;pre=Thrt;InThreading(T);pre-rchild=Thrt;/处理最后一个结点 pre-RTag=Thread;Thrt-rchild=pre;,void InThreading(BiThrTree p)if(p)/对以p为根的非空二叉树进行线索化 InThreading(p-lchild);/左子树线索化 if(!p-lchild)/建前驱线索 p-LTag=Thread;p-

26、lchild=pre;if(!pre-rchild)/建后继线索 pre-RTag=Thread;pre-rchild=p;pre=p;/保持 pre 指向 p 的前驱 InThreading(p-rchild);/右子树线索化/if/InThreading,6.4 树和森林 的表示方法,6.4.1 树的存储结构,一、双亲表示法(顺序存储),二、孩子链表表示法,三、树的二叉链表(孩子-兄弟)存储表示法,A,B,C,D,E,F,G,0 A-11 B 02 C 03 D 04 E 2 5 F 26 G 5,根的位置:r=0总结点数:n=7,data parent,一、双亲表示法:,typedef

27、struct PTNode TElemType data;int parent;/双亲位置域 PTNode;,data parent,#define MAX_TREE_SIZE 100,结点结构:,C语言的类型描述:,typedef struct PTNode nodes MAX_TREE_SIZE;int r,n;/根结点的位置和结点个数 PTree;,树结构:,特点:求父亲容易,求孩子难,如何求孩子?,需要遍历整个数组,找出父亲域等于其数组下标的所有结点。,二、孩子表示法:,1、多重链表:(1)以结点的度作为孩子链域,data degree child1 child2 childd,(2)

28、以树的度作为孩子链域,缺点:结点不同构,给操作带来麻烦,缺点:结点同构,但空链域较多,A,B,C,D,E,F,G,0 A1 B2 C3 D4 E5 F6 G,r=0n=7,data firstchild,1 2 3,4 5,6,-1000224,找孩子容易找父亲难,parent,2、孩子链表:,typedef struct CTNode int child;struct CTNode*next;*ChildPtr;,孩子结点结构:,child next,C语言的类型描述:,typedef struct TElemType data;ChildPtr firstchild;/孩子链的头指针 CT

29、Box;,表头结点结构,data firstchild,int parent;,data parent firstchild,typedef struct CTBox nodesMAX_TREE_SIZE;int n,r;/结点数和根结点的位置 CTree;,树结构:,A,B,C,D,E,F,G,AB C E D F G,root,AB C E D F G,三、树的二叉链表(孩子-兄弟)存储表示法,typedef struct CSNode TElemType data;struct CSNode*firstchild,*nextsibling;CSNode,*CSTree;,C语言的类型描述

30、:,结点结构:,firstchild data nextsibling,6.4.2 树和二叉树的转换,将树转换成二叉树,加线:在兄弟之间加一连线抹线:对每个结点,除了其左孩子外,去除与其余孩子之间的关系旋转:以树的根结点为轴心,将整树顺时针转45,树转换成的二叉树其右子树一定为空,将二叉树转换成树加线:若p结点是双亲结点的左孩子,则将p的右孩子,右孩子的右孩子,沿分支找到的所有右孩子,都与p的双亲用线连起来抹线:抹掉原二叉树中双亲与右孩子之间的连线调整:将结点按层次排列,形成树结构,要求 掌握将一棵树转化为二叉树方法 将转化二叉树还原为一棵树方法,森林和二叉树的对应关系,设森林 F=(T1,T

31、2,Tn);T1=(root,t11,t12,t1m);,二叉树 B=(LBT,Node(root),RBT);,。,T1,T2,T3,T4,Tn,森林转换成二叉树将各棵树分别转换成二叉树将每棵树的根结点用线相连以第一棵树根结点为二叉树的根,再以根结点为轴心,顺时针旋转,构成二叉树型结构,二叉树转换成森林抹线:将二叉树中根结点与其右孩子连线,及沿右分支搜索到的所有右孩子间连线全部抹掉,使之变成孤立的二叉树还原:将孤立的二叉树还原成树,由森林转换成二叉树的转换规则为:,若 F=,则 B=;否则,由 ROOT(T1)对应得到 Node(root);由(t11,t12,t1m)对应得到 LBT;由(

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

33、后根遍历各棵子树,然后访问根结点。,若树不空,则自上而下自左至右访问树中每个结点。,先根遍历时顶点的访问次序:,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,B C DE F G H I J K,1森林中第一棵树的根结点;,2森林中第一棵树的子树森林;,3森林中其它树构成的森林。,森林由三部分构成:,1.先序遍历,森林的遍历,若森林不空,则访问森林中第一棵树的根结点;先序遍历森林中第一棵树的子树森林;先序遍历森林中(除第一棵树之外)其 余树构成的森林。,

34、即:依次从左至右对森林中的每一棵树进行先根遍历。,中序遍历,若森林不空,则中序遍历森林中第一棵树的子树森林;访问森林中第一棵树的根结点;中序遍历森林中(除第一棵树之外)其 余树构成的森林。,即:依次从左至右对森林中的每一棵树进行后根遍历。,树的遍历和二叉树遍历的对应关系?,先根遍历,后根遍历,树,二叉树,森林,先序遍历,先序遍历,中序遍历,中序遍历,小结树和森林的遍历树的遍历遍历按一定规律走遍树的各个顶点,且使每一顶点仅被访问一次,即找一个完整而有规律的走法,以得到树中所有结点的一个线性排列常用方法先根(序)遍历:先访问树的根结点,然后依次先根遍历根的每棵子树后根(序)遍历:先依次后根遍历每棵

35、子树,然后访问根结点按层次遍历:先访问第一层上的结点,然后依次遍历第二层,第n层的结点,先序遍历:,后序遍历:,层次遍历:,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,讨论:若采用“先转换,后遍历”方式,结果是否一样?,d e c b a,a b c d e,b d c e a,1.树的先序遍历二法相同;2.树的后序遍历相当于对应二叉树的中序遍历;3.树没有中序遍历,因为子树无左右之分。,结论:,讨论:若采用“先转换,后遍历”方式,结果是否相同?,森林:,先序

36、序列:,中序序列:,A B C D E F G H I J,B C D A F E H J I G,A B C D E F G H I J,B C D A F E H J I G,结论:森林的先序和中序遍历在两种方式下的结果相同。,结论:当以二叉链表做树的存储结构时,树的先根序列和后根序列可借用二叉树的先序遍历和后序遍历的算法实现之;对于森林也一样。,6.6 赫夫曼树及其应用,最优树的定义 如何构造最优树 赫夫曼编码,一、最优树的定义,树的路径长度定义为:树中每个结点的路径长度之和。,结点的路径长度定义为:从根结点到该结点的路径上 分支的数目。,路径:从一个结点到另一个结点之间的若干个分支;路

37、径长度:路径上的分支数目称为路径长度;,结点的权:根据应用的需要可以给树的结点赋某种意义的实数称权值;树的带权路径长度=树中所有叶子结点的带权路径之和;通常记作 n WPL=wk Lk k=1赫夫曼树:假设有n个权值(w1,w2,wn),构造有n个叶子结点的严格二叉树,每个叶子结点有一个 wi 作为它的权值。则带权路径长度最小的严格二叉树称为最优二叉树(赫夫曼树)。,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,WPL(T)=9 2+7 2+5 2+2 3+4 3=60,如何构造赫夫曼树?,根据给定的

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

39、2,5,7,9,2,5,7,16,6,7,13,29,WPL=6*2+7*2+9*2+5*3+2*3=12+14+18+15+6=65,9,16,3、哈夫曼树构造程序,一棵有n个叶子结点的Huffman树有2n-1个结点采用顺序存储结构一维结构数组存储结点信息结点类型定义为:typedef struct int weight;int parent,lchild,rchild;HTNode,*HuffmanTree;,void Huffman(HuffmanTree/初始化HT数组,for(i=n+1;i=m;+i)/建赫夫曼树 Select(HT,i-1,s1,s2);/在HT1.i-1选择p

40、arent为-1且weight最小的两个结点,其序号分别为s1,s2HTs1.parent=i;HTs2.parent=i;HTi.lchild=s1;HTi.rchild=s2;HTi.weight=HTs1.weight+HTs2.weight;/Huffman,构造赫夫曼树,例:2,5,1,7,6,123456789,Weight parent lchild rchild,Huffman树应用 _最佳判定树,在进行数据通讯时,涉及数据编码问题。所谓数据编码就是数据与二进制字符串的转换。例如:邮局发电报:,例 要传输的原文为A B A C C D A等长编码 A:00 B:01 C:10

41、D:11发送方:ABACCDA 转换成 00010010101100接收方:00010010101100 还原为 ABACCDA,三、赫夫曼编码,不等长编码 A:0 B:00 C:1 D:01发送方:将ABACCDA 转换成 000011010接收方:000011010 转换成,A A A A C C D AB B C C D A,A的编码是B的前缀,指的是,任何一个字符的编码都不是同一字符集中另一个字符的编码的前缀。如A B C D 四个字符的使用率由高到低,编码为A-0 B-10 C-110 D-111,因此,若设计长短不等的编码,则必须是任何一个字符的编码都不是另一个字符的编码的前缀,这

42、种编码称做前缀编码。,例如:(A B C D)字符使用频度作为权值:(3,1,2,1)构造哈夫曼树。,将该哈夫曼树所有左分支标记0,所有右分支标记1;,利用赫夫曼树可以构造一种不等长的二进制编码,并且构造所得的赫夫曼编码是一种最优前缀编码,即使所传电文的总长度最短。,A:0 B:110C:10 D:111,Huffman编码:数据通信用的二进制编码思想:根据字符出现频率编码,使电文总长最短编码:根据字符出现频率构造Huffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列,例 要传输的字符集 D=C,A,S,

43、T,;字符出现频率 w=2,4,2,3,3,T:00;:01A:10C:110S:111,译码:从Huffman树根开始,从待译码电文中逐位取码。若编码是“0”,则向左走;若编码是“1”,则向右走,一旦到达叶子结点,则译出一个字符;再重新从根出发,直到电文结束,例 电文是CAS;CAT;SAT;AT 其编码“11010111011101000011111000011000”电文为“1101000”译文只能是“CAT”,例:ABCDEFGH在一段电文中出现的概率如下:7,19,2,6,32,3,21,10将概率作为叶子结点的权值,构造一棵哈夫曼树。,100,60,28,11,17,32,6,7,

44、10,5,2,3,A B C D E F G H7,19,2,6,32,3,21,101010 00 10000 1001 11 10001 01 1011,WPL=6*4+2*5+3*5+7*4+10*4+32*2+19*2+21*2=261,0,0,0,0,0,0,1,1,1,1,1,1,电文:101111110001,译文:HEEBG,Typedef char*HuffmanCode;void HuffmanCoding(HuffmanTree/编码的结束符,for(i=1;i=n;+i)/逐个字符求赫夫曼编码 start=n-1;/编码结束符位 for(c=i,f=HTi.parent

45、;f!=0;c=f,f=HTf.parent)/从叶子到根逆向求编码 if(HTf.lchild=c)cd-start=“0”;/c是f的左孩子 else cd-start=“1”;/c是f的右孩子HCi=(char*)malloc(n-start)*sizeof(char*);Strcpy(HCi,/HuffamCoding,void HuffmanCoding2(HuffmanTree/访问标志,While(p)if(HTp.weight=0)/向左 HTp.weight=1;if(HTp.lchild!=0)p=HTp.lchild;cdcdlen+=“0”;else if(HTp.rc

46、hild=0)HCp=(char*)malloc(cdlen+1)*sizeof(char);cdcdln=“0”;strcpy(HCp,cd);,else if(HTp.weight=1)/向右 HTp.weight=2;if(HTp.rchild!=0)p=HTp.rchild;cdcdlen+=“1”;else HTp.weight=0;p-HTp.parent;-cdlen;/退到父结点,编码长度减1/while/HuffmanCoding2,1.熟练掌握二叉树的结构特性,了解相应的证明方法。2.熟悉二叉树的各种存储结构的特点及适用范围。3.遍历二叉树是二叉树各种操作的基础。实现二叉树

47、遍历的具体算法与所采用的存储结构有关。掌握各种遍历策略的递归算法,灵活运用遍历算法实现二叉树的其它操作。,4.理解二叉树线索化的实质是建立结点与其在相应序列中的前驱或后继之间的直接联系,熟练掌握二叉树的线索化过程以及在中序线索化树上找给定结点的前驱和后继的方法。二叉树的线索化过程是基于对二叉树进行遍历,而线索二叉树上的线索又为相应的遍历提供了方便。,5.熟悉树的各种存储结构及其特点,掌握树和森林与二叉树的转换方法。建立存储结构是进行其它操作的前提,因此应掌握 1 至 2 种建立二叉树和树的存储结构的方法。6.学会编写实现树的各种操作的算法。7.了解最优树的特性,掌握建立最优树和哈夫曼编码的方法

48、。,例 写出如下树型结构的前序遍例的算法typedef struct CTNode/儿子链表的结构 int child;struct CTNode*next;*ChildPtr;typedef struct/树节点的结构 Elem data;int parent;ChildPtr firstchild;/孩子链的头指针 CTBox;typedef struct/树的结构 CTBox nodesMAX_TREE_SIZE;int n,r;/结点数和根结点的位置 CTree;,Status PreTree(CTree*T,int r,visit)if(!T-nodesr)visit(T-nodes

49、r.data);for(p=T.nodesr.firstchild;p;p=p-next)PreTree(T,p-child,visit);return OK;Status Ptree(CTree T)/前序遍例算法 PreTree(,练习题6.29 讲述练习题6.30 先序序列中U在V之前,则U为V的祖先或左兄弟 后序序列中U在V之后,则U为V的祖先或右兄弟 所以U为V的祖先,int Is_Descendant(int u,int v)/在孩子存储结构上判断u是否v的子孙,是则返回1,否则返回0if(u=Rv|u=Lv)return 1;elseif(Lv)if(Is_Descendant(

50、u,Lv)return 1;if(Rv)if(Is_Descendant(u,Rv)return 1;/这是个递归算法return 0;/Is_Descendant,6.33,int Is_Descendant_P(int u,int v)/在双亲存储结构上判断u是否v的子孙,是则返回1,否则返回0for(p=u;p!=v/Is_Descendant_P,6.34,6.36,int Bitree_Sim(Bitree B1,Bitree B2)/判断两棵树是否相似的递归算法if(!B1/Bitree_Sim,int c,k;/这里把k和计数器c作为全局变量处理 c=0void Get_PreS

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

当前位置:首页 > 建筑/施工/环境 > 农业报告


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号