630 likes | 870 Views
第 6 章 树. 6.1 树的概念与表示 6.2 二叉树的概念与性质 6.3 二叉树的存储结构 6.4 二叉树的遍历 6.5 线索二叉树 6.6 树的存储结构 6.7 树、森林与二叉树的转换 6.8 哈夫曼树及其应用 小 结. 本章学习目标 树型结构是一类重要的非线性结构,在计算机领域有着广泛的应用,如数据库系统中的信息组织等。. 通过本章学习,应掌握如下内容: 树的定义及相关术语。 二叉树的性质、存储结构及其遍历算法。 树的存储结构,树和森林与二叉树的转换。 哈夫曼算法及应用。.
E N D
第6章 树 • 6.1 树的概念与表示 • 6.2 二叉树的概念与性质 • 6.3 二叉树的存储结构 • 6.4 二叉树的遍历 • 6.5 线索二叉树 • 6.6 树的存储结构 • 6.7 树、森林与二叉树的转换 • 6.8 哈夫曼树及其应用 • 小 结 1
本章学习目标树型结构是一类重要的非线性结构,在计算机领域有着广泛的应用,如数据库系统中的信息组织等。本章学习目标树型结构是一类重要的非线性结构,在计算机领域有着广泛的应用,如数据库系统中的信息组织等。 通过本章学习,应掌握如下内容: 树的定义及相关术语。 二叉树的性质、存储结构及其遍历算法。 树的存储结构,树和森林与二叉树的转换。 哈夫曼算法及应用。 2
6.1.1 树的定义 树(Tree)是n个(n≥0)个结点组成的有限集合T,当T为空集时称为空树,否则它满足如下两个条件: (1) 有且仅有一个特定的称为根(Root)的结点。 A A C B D G E F H I 非树 K 树 6.1 树的概念与表示 (2) 除根结点外的其余结点可分为 m(m>0)个互不相交的子集 T1,T2,…,Tm,其中每个子集Ti本身又是一棵树,并称其为根的子树。 3
树具有下面两个特点: (1)树的根结点没有前驱结点,除根结点之外的所有结点有且只有一个前驱结点。 (2)树中所有结点可以有零个或多个后继结点。 C:\ DOS USER WIN TEM WEB HEL FONT COM TEMP SYS 4
6.1.2 树的相关术语 结点:一个数据元素及若干个指向其子树的分支。结点的度:结点所拥有的子树的个数称为该结点的度。树的度:树中结点度的最大值。 A B C D E F G H I J K L 叶子结点:度为0的结点称为叶子结点,或者称为终端结点。分支结点:度不为0的结点称为分支结点或非终端结点。 孩子、双亲和兄弟结点:树中结点子树的根称为该结点的孩子。相应地,该结点称为孩子结点的双亲。 5
具有同一个双亲的孩子结点互称为兄弟。祖先、子孙:从树中根结点到某一结点所经过的分支上的所有结点称为该结点的祖先。反之,以某结点为根的子树中的任一结点都称为该结点的子孙。 结点的层数:规定树的根结点的层数为1,其余结点的层数等于它的双亲结点的层数加1。树的深度:树中所有结点的最大层数称为树的深度或高度。 A B C D E F G H I J K L 有序树和无序树:如果一棵树中结点的各子树从左到右是有次序的——有序树;反之,——无序树。 森林:m(m≥0)棵互不相交的树的集合称为森林。 6
6.1.3 树的表示 树的表示有四种方法,直观表示法、嵌套集合表示法、凹入表示法、广义表表示法。 A C A B D E H F C G I B E I A B C D H G D F E F G H I J K L (A(B(D,E(H),F),C(G,I))) 广义表表示法 7
6.2.1 二叉树的基本概念 定义:二叉树是n个(n≥0)结点的集合,该集合或者为空、或者由一个根结点和两个分别称为左子树和右子树的互不相交的二叉树组成,当集合为空时,称该二叉树为空二叉树。 二叉树具有五种基本形态:空二叉树、仅有根结点的二叉树、右子树为空的二叉树、左子树为空的二叉树和左右子树均不为空的二叉树。 左子树 右子树 (b) 仅有根结点 (c) 右子树为空 (d) 左子树为空 (e) 左右子树均不为空 二叉树的五种基本形态 左子树 右子树 6.2 二叉树的概念与性质 8
6.2.2 二叉树的重要性质 二叉树有以下重要性质。 性质1 二叉树的第i(i≥1)层上最多有2 i -1个结点。 对非空二叉树,第一层为根结点,最多只有一个结点,即20个;显然第二层最多有21个;以此类推,第i 层上最多只有2i-1个结点。 性质2 在一棵深度为k的二叉树中,最多具有2 k-1个结点。 1 3 2 6 4 5 7 A M F A B C E G D I H K J L N O B C 11 12 13 15 14 10 8 9 E F 9
1 3 2 6 4 5 7 A J F A B D E G C I K H M L N O B C 11 12 13 15 14 10 8 9 E F 性质2 在一棵深度为k的二叉树中,最多具有2 k-1个结点。 证明:由性质1可知,第i层的结点数最多为2 i-1(1≤i≤k),则深度为k的二叉树的结点数最多为: 性质3对于一棵非空的二叉树,如果叶子结点数为n0,度数为2的结点数为n2,则有: n0=n2+1。 10
证明:设度为i的结点有ni个(其中:i=0,1,2),总结点个数为 n,总分支数为e,则根据二叉树的定义,有如下的式子: n = n0 + n1 + n2 e = 2n2 + n1 = n - 1 将两式结合有: 2n2 + n1 = n0 + n1 + n2 - 1 即有 n0 = n2 + 1 1 3 2 6 4 5 7 A K L F H I J M D E N O C B A G B C 11 12 13 15 14 10 8 9 E F 一棵深度为k且有2 k-1个结点的二叉树称为满二叉树。 当深度为k,含有n个结点的二叉树,当且仅当其每个结点的编号与相应满二叉树结点顺序编号从1~n互相对应时,则称此二叉树为完全二叉树。 11
1 2 3 4 5 6 7 1 1 14 15 8 9 12 13 10 11 2 3 (a) 满二叉树 C E F C B A D F G H I J E B A O K D H G F D E B I C A M J L N 3 2 7 6 5 4 7 4 6 8 9 10 (b) 完全二叉树 (c) 非完全二叉树 12
性质4 具有n个结点的完全二叉树的深度k为。 性质5 对于具有n个结点的完全二叉树,如果按照从上至下和从左到右的顺序对所有结点从1开始顺序编号,则对于任意的序号为i的结点有: (1)若i =1 则该结点为根结点i,无双亲; (2)若i>1, 则i结点的双亲为; (3)若2i≤n, 则i结点的左孩子为2i,否则没有左孩子。 (4)若2i+1≤n, 则i结点的右孩子为2i+1,否则没有右孩子。 (5)i结点所在层次为 13
二叉树有两种存储结构:顺序存储结构和链式存储结构。二叉树有两种存储结构:顺序存储结构和链式存储结构。 6.3.1 顺序存储结构 二叉树的顺序存储,就是把所有结点按照一定的顺序存放到一组连续的存储单元中。一般是按照完全二叉树中结点编号将结点自上而下、自左至右的顺序存储。可以看出,数组下标和结点的编号相对应,即结点在数组中的位置隐含了结点之间的关系。 A B C D E F G H I J 1 D A C B F G E I H J 2 3 1 2 3 4 5 6 7 8 9 10 7 6 5 4 8 9 10 6.3 二叉树的存储结构 数组下标 14
6.3.2 链式存储结构 二叉树的链式存储结构是指用链表的形式来存储二叉树,即用指针来指示元素之间的逻辑关系。最常用的有二叉链表和三叉链表。 1. 二叉链表存储 每个结点由三个域组成,一个数据域和两个指针域。两个指针lchild和rchild分别指向其左孩子和右孩子结点。 lchild data rchild 结点结构描述为: typedef struct BiTNode{ ElemType data; /*数据域*/ struct BiTNode *lchild, *rchild; /*左右指针域*/ }BTNode; 15
2. 三叉链表存储 每个结点由四个域组成,具体结构如图6-6所示: lchild data rchild parent 其中data、lchild以及rchild三个域的意义同二叉链表结构相同,parent为指向该结点双亲结点的指针。这种存储结构既便于查找孩子结点,又便于查找双亲结点;但是,相对于二叉链表存储结构而言,它增加了空间开销。 typedef struct BiTNode{ ElemType data; /*数据域*/ struct BiTNode *lchild, *rchild,*parent; /*左、右及双亲指针域*/ }BTNode_p; 16
二叉树T的二叉链表表示,其中bt是一个*BTNode类型的变量;三叉链表表示,其中bt_p是*BTNode_p类型的变量。 bt bt_p A B D C E F G 三叉链表 二叉链表 ^ G ^ ^ E ^ A ^ ^ F ^ C ^ D B ^ ^ D ^ F ^ ^ E ^ C B ^ A ^ G ^ 二叉树 17
6.3.3 建立二叉树的二叉链表 建立二叉链表有多种方法,比较直观的方法是利用二叉树的性质5来创建二叉链表。 对于一棵任意的二叉树,先按满二叉树对其进行编号。这时结点的编号满足性质5的关系。算法实现时,设置一个辅助向量数组p存放指向结点的指针。如p[i] 用来存放编号为i的结点的指针。当输入一个结点s信息,即创建了该结点,同时将该结点的指针保存在向量p[i]中。当i=1时,为根结点。当i>1时,其双亲结点编号j=i/2,若i为奇数,则它是双亲的右孩子,有p[ j ]-> rchild=s;若i为偶数,则它是双亲的左孩子,有p[ j ]->lchild=s;。这样就让每个结点和其双亲相连,建立了二叉链表。 A B C D E F G H I J 1 2 3 4 5 6 7 8 9 10 18
【算法6.1】建立二叉树的二叉链表存储。 # define MAXNUM 15 /*最大结点数*/ BTNode *p[MAXNUM+1]; /* 辅助向量*/ BTNode *Create_BiTree (void) { printf("\n enter i,ch:"); /* 假设数据域为字符类型*/ scanf (“%d,%c”,&i,&ch); /*按照完全二叉树型态的结点编号顺序输入s结点信息*/ while (i!=0 && ch!=’#’) { s=( BTNode *)malloc(sizeof(BTNode)); s->data=ch; s->lchild=s->rchild=NULL; /*创建结点数据域为ch,指针域为空*/ p[i]=s; /*将结点放入向量p*/ 19
if (i==1) t=s; else { j=i/2; if(i%2==0) p[j]->lchild=s; /*i为偶数,为双亲左孩子*/ else p[j]->rchild=s; /*i为奇数,为双亲右孩子*/ } printf("\n enter i,ch:"); scanf(“%d,%c”,&i,&ch); /*输入下一个结点信息*/ } Return t; /*返回根结点*/ } /* Create_Bt */ A B C D E F G H I J 1 2 3 4 5 6 7 8 9 10 20
二叉树的遍历是指按照某种顺序访问树中的每个结点,并且每个结点仅被访问一次的过程。或者说是树的线性化。二叉树的遍历是指按照某种顺序访问树中的每个结点,并且每个结点仅被访问一次的过程。或者说是树的线性化。 一棵二叉树是由根结点、左子树和右子树三部分组成。若以N、L、R分别表示访问根结点、遍历根结点的左子树、遍历根结点的右子树,则二叉树的遍历方式就有以下六种:NLR、LNR、 LRN 、NRL、RNL和RLN。常用的是前三种方式,即NLR(称为前序或先根遍历)、LNR(称为中序或中根遍历)和LRN(称为后序或后根遍历)。 A B C E F 6.4 二叉树的遍历 1.前序遍历 前序遍历的递归过程为: 若二叉树为空,遍历结束。否则 (1)访问根结点; (2)前序遍历左子树; (3)前序遍历右子树。 21
【算法6.2】递归算法如下: void preorder1(BTNode *bt) { if (bt) { printf(bt->data);/* 访问根结点 */ preorder1(bt->lchild);/* 前序遍历左子树 */ preorder1(bt->rchild);/* 前序遍历右子树 */ } } /* preorder1 */ 如图按前序遍历所得到的结点序列为: A B D G C E。 A A B D C E G B C D E G 22
【算法6.3】二叉树的前序遍历的非递归算法。 void preorder2(BTNode *bt) { p=bt;top=-1; while(p||top) { if(p) /* 二叉树非空 */ { printf(p->data); /*访问根结点,输出结点*/ ++top ;s[top]=p ;/*根指针进栈*/ p=p->lchild ;} /*p移向左孩子*/ else /*栈非空*/ { p=s[top] ;--top ;/*双亲结点出栈*/ p=p->rchild ;} /*p移向右孩子*/ } }/* preorder2 */ 23
对于图6-8(a)中的二叉树,遍历过程中所使用栈中s的内容变化如图6-9所示。对于图6-8(a)中的二叉树,遍历过程中所使用栈中s的内容变化如图6-9所示。 D B A G B B B B A A A A A E A C C C 图6-9 栈s的内容变化过程 24
2.中序遍历 中序遍历的递归过程为: 若二叉树为空,遍历结束。否则 (1)中序遍历左子树; (2)访问根结点; (3)中序遍历右子树。 【算法6.4】二叉树的中序遍历的递归算法。 void Inorder(BTNode *bt) { Inorder(bt->lchild);/* 中序遍历根结点 */ printf(bt->datd); /* 访问根结点 */ Inorder(bt->rchild); /* 中序遍历右子树*/ } /* inorder */ 对于图所示的二叉树,按中序遍历所得到的结点序列为: D G B A E C A B C D E G 25
3.后序遍历 后序遍历的递归过程为: 若二叉树为空,遍历结束。否则 (1)后序遍历左子树; (2)后序遍历右子树。 (3)访问根结点; 【算法6.5】二叉树的后序遍历的递归算法 void Postorder(BTNode *bt) {Postorder(bt->lchild); /* 后序遍历根结点 */ Postorder(bt->rchild);/* 访问根结点 */ printf(bt->datd); /* 后序遍历右子树*/ } /* Postorder*/ 对于图所示的二叉树,按后序遍历所得到的结点序列为: G D B E C A。 A B C D E G 26
任意一棵二叉树结点的前序和中序和后序序列都是唯一的。反过来,若已知结点的前序和中序序列(或后序和中序序列),便能够唯一确定一棵二叉树。任意一棵二叉树结点的前序和中序和后序序列都是唯一的。反过来,若已知结点的前序和中序序列(或后序和中序序列),便能够唯一确定一棵二叉树。 A A B C B D C E F G D E G F 例如,已知一棵二叉树的前序序列与中序序列分别为: A B D C E F G B D A E C G F 根 以A为根的左、右子树先序序列示意图 27
A B C D E G 4.按层次遍历二叉树 所谓二叉树的层次遍历是指从二叉树的第一层开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。图所示的二叉树,按层次遍历所得到的结果序列为:A B C D E G。 为了实现按层次遍历,可设置一个队列结构,从二叉树的根结点开始,首先将根结点指针入队列,然后反复执行下面两个操作,直到队列空为止。 (1) 从队头取出一个元素,每取一个元素,访问该元素所指向的结点;(2) 若该元素所指结点的左、右孩子结点非空,则将该元素所指结点的左孩子指针和右孩子指针顺序入队。 28
在下面的层次遍历算法中,二叉树以二叉链表存放,一维数组Queue[MAXNODE]用以实现队列,变量front和rear分别表示当前队头元素和队尾元素在数组中的位置。在下面的层次遍历算法中,二叉树以二叉链表存放,一维数组Queue[MAXNODE]用以实现队列,变量front和rear分别表示当前队头元素和队尾元素在数组中的位置。 【算法6.6】二叉树的层次遍历算法 void LevelOrder(BTNode* bt) /*层次遍历二叉树bt*/ { BTNode* Queue[MAXNODE]; int front,rear; if (bt==NULL) return; front=0; /*非循环队列*/ rear=0; queue[rear++]=bt; A B C D E G 29
while(front!=rear) {Visite(queue[front]->data); /*访问队首结点的数据域*/ if (queue[front]->lchild!=NULL) /*将队首结点的左孩子结点入队列*/ { rear++; queue[rear]=queue[front]->lchild; } if (queue[front]->rchild!=NULL) /*将队首结点的右孩子结点入队列*/ {rear++; queue[rear]=queue[front]->rchild; } front++; } /* while */ } A B C D E G 30
6.5.1 线索二叉树的定义及结构 1.线索二叉树的定义 以某种方式对二叉树进行遍历时,可以得到结点的一个线性序列。利用二叉树的二叉链表存储结构中的那些空指针域来指示前驱和后继结点的指针被称为线索,加了线索的二叉树称为线索二叉树。 A A B D C E F G B C D E F G 1 G 1 0 B 1 1 E 1 ^ 1 D 0 0 C 0 0 A 0 1 F 1 ^ 6.5 线索二叉树 31
2.线索二叉树的结构 n个结点的二叉链表中有2n个指针域,另外n+1个指针域为空而被闲置。因此,可以利用这些空闲的指针域:无左孩子时,令其左指针域指向其直接前驱结点;当该结点无右孩子时,令其右指针域指向其直接后继结点。对于那些非空的指针域,则仍然指向该结点左、右孩子。这样,就得到了线索二叉树。把二叉树改造成线索二叉树的过程称为线索化。 可在每个结点中增设两个标志位Ltag和Rtag。这样结点的结构为: lchild Ltag data Rtag rchild 0 A 0 0 B 1 0 C 0 ^ 1 D 0 1 E 1 1 F 1 ^ 1 G 1 32
其中: 0 lchild指向结点的左孩子 1 lchild指向结点的直接前驱结点 0 rchild指向结点的右孩子 1 rchild指向结点的直接后继结点 对应的描述为: typedef struct ThredNode { ElemType data; struct ThredNode *lchild,*rchild; unsigned Ltag,Rtag; }ThrNode; lchild Ltag data Rtag rchild Ltag= Ltag= 33
6.5.2 中序线索二叉树操作 1.建立中序线索二叉树 建立线索二叉树,或者说对二叉树线索化,就是将二叉链表中的空指针改为指向前驱和后继结点的线索,而前驱和后继只能在遍历过程中才能得到。因此,中序线索二叉树的建立是在中序遍历二叉树过程中,检查当前结点的左、右指针域是否为空,如果为空,将它们改为指向前驱结点或后继结点的线索,即建立了线索。 0 A 0 0 B 1 0 C 0 ^ 1 D 0 1 E 1 1 F 1 ^ 1 G 1 34
【算法6.7】建立中序线索二叉树的递归算法 void InOrderThr(ThrNode *t) /*中序线索二叉树t */ { if ( t ) { InOrderThr (t->lchild ); /*线索化左子树 */ if (t ->lchild= =NULL) { t -> Ltag=1; t -> lchild=pre; } if (pre -> rchild=NULL) /*建立pre结点的后继线索*/ { pre -> Rtag=1; pre -> rchild=t; } pre=t; InOrderThr( t -> rchild); /*线索化右子树*/ } /* InOrderThr */ 35
2.遍历中序线索二叉树 要遍历线索二叉树,关键是找到结点的后继。有以下两种情况: (1)如果该结点的右标志为1,那么其右指针域所指向的结点便是它的后继结点; (2)如果该结点的右标志为0,该结点有右孩子,它的后继结点是以该结点的右孩子为根结点的子树的最左下的结点。【算法6.8】中序线索二叉树的遍历算法。 void InVisitThr(ThrNode *t) 0 A 0 0 B 1 0 C 0 ^ 1 D 0 1 E 1 1 F 1 ^ 1 G 1 36
{ p=t ; if(p!= NULL) { while(p->lchild !=NULL) p=p->lchild ; printf(p->data); /*中序序列第一个结点*/ while(p->rchild!=NULL) { if (p->Rtag==1) p=p->rchild; /*右标志为1,则右指针域所指向的结点是它的后继结点 */ else { p=p->rchild; /*右标志为0,表明该结点有右子树*/ while(p->Ltag!=1) p=p->lchild; } printf(p->data); } }/* InVisitThr */ 37
3. 在中序线索二叉树上查找任意结点的中序前驱结点 有以下两种情况: (1)如果该结点的左标志为1,那么其左指针域所指向的结点就是它的前驱结点; (2)如果该结点的左标志为0,表明该结点有左孩子,它的前驱结点是以该结点的左孩子为根结点的子树的最右结点。 【算法6.9】在中序线索二叉树上找结点p的中序前驱结点 ThrNode InPreNode(ThrNode p) { ThrNode pre; pre=p->lchild; if (p->Ltag !=1) while (pre->Rtag==0) pre=pre->rchild; return(pre); } 38
1.双亲表示法 因为树中的每个结点都有唯一的一个双亲结点,可用一个一维数组来存储树中的各个结点,其中包括结点本身的信息以及结点的双亲结点在数组中的序号。其结点表示可描述为: Typedef struct PtNode { ElemType data; int parent; }PTNode; 求某个结点的双亲结点是非常容易的,但要查找某一个结点的孩子时,则要访问整个数组。 0 A -1 B 0 C 0 D 1 E 1 F 2 I 2 J 2 G 4 H 4 1 A 2 3 4 5 B C 6 7 8 D E F I J 9 G H 6.6 树的存储结构 39
2.孩子表链表示法 将每个结点的孩子用链表连接起来,构成一个单链表。单链表的结构由两个域组成,一个存放孩子结点在一维数组中的序号,另一个域指向下一个孩子。 1 0 A B C D ∧ E F ∧ I ∧ J ∧ G ∧ H ∧ 2 ∧ 1 3 4 ∧ 2 5 6 7 ∧ 3 4 9 ∧ 8 5 6 7 8 9 40
∧ C ∧ ∧ G.. ∧ H ∧ A ∧ B C ∧ ∧ D E ∧ ∧ F ∧ C 3.孩子-兄弟表示法 这种表示法以二叉链表作为树的存储结构,其结点结构为:一个数据域和两个指针域,其中一个指针指向它的第一个孩子结点,另一个指针指向它的兄弟结点。 41
6.7.1 树和森林转换为二叉树 1.一般树转换为二叉树 (1)连线:在各兄弟之间用虚线相连。 (2)抹线:对树中的每个结点,只保留它与第一个孩子结点之间的连线,删去它与其它孩子结点之间的连线。 (3)旋转:以树的根结点为轴心,将整棵树顺时针转动45°度角,使之结构层次分明。 F D C B A H G E G D C B A A E H H H F E D C B A G G F E D C B F 6.7 树、森林与二叉树的转换 42
A J L K F E C D B A H I F E C H I D H G K J L G G D B A L J K F E B I C (a) 一般树的森林 (c) 最终的二叉树 (b) 二叉树的森林 43
2.森林转换为二叉树 (1)将森林中的每棵树转换成相应的二叉树,形成二叉树的森林。 (2)按森林中的先后顺序,依次把后一棵二叉树的根结点作为前一棵二叉树根结点的右孩子。 6.7.2 二叉树还原为树和森林 1.二叉树还原为一般树步骤: (1)加线。若某结点是双亲结点的左孩子,则将该结点的右孩子以及当且仅当连续地沿着右孩子的右链不断搜索到的所有右孩子,都分别与结点的双亲结点用虚线连接。 (2)抹线:把原二叉树中所有双亲结点与其右孩子连线抹去。 (3)整理、旋转:把虚线改为实线;把结点按层次排列。 44
2.二叉树还原为森林 (1)抹线;(2)还原. C D E F G I J H A B C D E F G I J B A I H C D E F G I J J A B C D E F G A H B H 二叉树还原为树的过程示意图 45
哈夫曼(Huffman)树,也称最优二叉树,是指一类带权路径最短的树。哈夫曼(Huffman)树,也称最优二叉树,是指一类带权路径最短的树。 1.哈夫曼树的基本概念 (1)路径、路径长度:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径,路径上的分支个数称为路径长度。 (2)树的路径长度:从根结点到树中每个结点的路径长度之和。 (3)结点的带权路径长度:从根结点到该结点的路径长度与该结点所带的权值的乘积。 (4)树的带权路径长度:树中所有叶子结点的带权路径长度之和。记为: 6.8 哈夫曼树及其应用 46
(5)哈夫曼树:设有n个权值 { W1,W2,…,Wn },构造一个具有n个叶子结点的二叉树,每个叶子结点带有权值Wi,在所有的二叉树中,带权路径长度WPL最小的二叉树称为哈夫曼树。 4 3 5 6 5 8 5 4 4 4 8 A E D B A C E B C B A D D E C 2 3 6 (a) (b) (c) 8 4 3 6 哈夫曼树 哈夫曼树 47
2.构造哈夫曼树 一棵二叉树要使其WPL值最小,必须使权值越小的叶子结点越远离根结点。 (1)根据给定的n个权值{W1,W2,…,Wn}构造n棵只有一个根结点的二叉树。从而得到一个二叉树的集合F={T1,T2,…,Tn}。 (2)在集合F中选取根结点权值最小的两棵二叉树作为一棵新二叉树的左、右子树,新的二叉树根结点的权值为左、右子树根结点权值之和。 4 E D B A C 3 5 4 8 6 (3)在集合F中删除作为左、右子树的两棵二叉树,并将新建立的二叉树加入到集合F中。 (4)重复(2)(3)两步,当F中只剩下一棵二叉树时,这棵二叉树就是所要建立的哈夫曼树。 48
例:W={3,4,5,8,6}的哈夫曼树的构造过程。为了直观起见,在图中把带权的叶子结点画成方形,其它非叶子结点仍为圆形。其带权路径长度为59。例:W={3,4,5,8,6}的哈夫曼树的构造过程。为了直观起见,在图中把带权的叶子结点画成方形,其它非叶子结点仍为圆形。其带权路径长度为59。 8 5 8 6 3 4 3 4 5 6 (a) (b) (c) 26 11 7 7 11 7 7 8 5 6 8 3 4 5 6 3 4 (d) (e) 3 4 5 8 6 11 15 15 49
weightpalc rc weightpalc rc 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 生成前的状态 生成后的状态 50