1、数据结构-第六章 树和二叉树,1,第六章 树和二叉树,6.1 树的定义和基本术语 6.2 二叉树及其存储结构 6.3 遍历二叉树 6.4 线索二叉树 6.5 赫夫曼树及其应用 6.6 树和森林 6.7 二叉树和树的应用示例 本章学习要点、习题与上机作业,数据结构-第六章 树和二叉树,2,6.1 树的定义和基本术语,树是一类重要的非线性数据结构,是以分支关系定义的层次结构。6.1.1 树的定义树是由n(n0)个结点组成的有限集合T,非空树满足:1)有一个称之为根(root)的结点。2)当n1时,除根以外的其余结点被分成m(m0)个互不相交的集合T1, T2 , Tm,其中每一个集合本身又是一棵树
2、,且称为根的子树。,数据结构-第六章 树和二叉树,3,6.1.2 树的逻辑表示方法,A,B C D,E F G H I,J,1层2层3层4层,特点 除根结点外,每个结点都仅有一个前趋(父)结点。,数据结构-第六章 树和二叉树,4,树的其它表示方法嵌套集合表示法(文氏图表示法),凹入表表示法,广义表表示法 A( B( E,F(J),G ),C,D( H,I ),ABEFJGCDHI,数据结构-第六章 树和二叉树,5,6.1.3 基本术语,结点的度 结点拥有的子树数目。 树的度 树的各结点度的最大值。 叶子(终端)结点 度为0的结点。 分支(非终端)结点 度不为0的结点。 内部结点 除根结点之外的
3、分支结点。 双亲与孩子(父与子)结点 结点的子树的根称为该结点的孩子;该结点称为孩子的双亲。 兄弟 属于同一双亲的孩子。 堂兄弟 双亲在同一层的结点互为堂兄弟。 结点的祖先 从根到该结点所经分支上的所有结点。 结点的子孙 该结点为根的子树中的任一结点。,数据结构-第六章 树和二叉树,6,结点的层次 表示该结点在树中的相对位置。根为第一层,其它的结点依次下推;若某结点在第L层上,则其孩子在第L+1层上。 树的深(高)度 树中结点的最大层次。 有序树 树中各结点的子树从左至右是有次序的,不能互换。否则,称为无序树。 路径长度 从树中某结点Ni出发,能够“自上而下地”通过树中结点到达结点Nj,则称N
4、i到Nj存在一条路径,路径长度等于这两个结点之间的分支数。 树的路径长度 从根到每个结点的路径长度之和。 森林 是m(m0)棵互不相交的树的集合。,数据结构-第六章 树和二叉树,7,6.1.4 树的基本操作,1)构造空树 InitTree(&T) 2)销毁已存在的树 DestroyTree(&T) 3)将树清为空树 ClearTree(&T) 4)求树的深度 TreeDepth(T) 5)求树的根 Root(T) 6)求指定结点的双亲 Parent(T, Cur_e) 7)求指定结点的最左孩子 LeftChild(T, Cur_e) 8)求指定结点的右兄弟 RightSibling(T, Cu
5、r_e) 9)插入子树c作为结点p的第i棵子树 InsertChild(&T,&p,i,c) 10)删除p结点的第i棵子树 DeleteChild(&T,&p,i) 11)遍历操作 TraverseTree(T, Visit(),数据结构-第六章 树和二叉树,8,6.2 二叉树及其存储结构 引入二叉树是作为一般树(森林)的规范形式,它与树(森林)可以建立一一对应,为解决树的问题提供方便。,6.2.1 二叉树的概念二叉树的定义 二叉树是n(n0)个结点的有限集合,它或为空树(n=0),或由一个根结点和两棵互不相交的左子树和右子树的二叉树组成。二叉树的特点 定义是递归的 0结点的度2 是有序树,左
6、子树,右子树,根结点,数据结构-第六章 树和二叉树,9,二叉树的五种基本形态,两种特殊的二叉树满二叉树 每一层上的结点数都是最大结点数。完全二叉树 只有最下面两层结点的度可小于2,而最下一层的叶结点集中在左边若干位置上。,1,2 3,4 5 6 7,数据结构-第六章 树和二叉树,10,6.2.2 二叉树的重要性质,性质1 二叉树的第i层上至多有2i-1(i1)个结点。 证明归纳法i=1时,只有一个根结点,20=1正确;假设第i-1层至多有2i-2 个结点,又二叉树每个结点的度至多为2 第i层上最大结点数是第i-1层的2倍,即2i-1 个。性质2 深度为k的二叉树至多有2k-1个结点(k1)。,
7、证明由性质1,可得深度为k 的二叉树最大结点数是,数据结构-第六章 树和二叉树,11,性质3 对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2 ,则n0 = n2 + 1。 证明以两种方式统计二叉树的分支数,设n1为T中度为1的结点数总度数即分支数B= n1+2n2又二叉树中,除根结点外,其余结点都只有一个分支进入,则B=n0+n1+n2-1 n0=n2+1性质4 具有n个结点的完全二叉树的深度为 log2n+1(或log2(n+1)。 证明设完全二叉树的深度为k,则2k-1-1 n 2k-1可得: 2k-1 n 2k 取对数: k-1 log2n k因k-1和k是相邻的整数,
8、有:k-1= log2n ,数据结构-第六章 树和二叉树,12,性质5 一棵具有n个结点的完全二叉树(又称顺序二叉树),对其结点按层从上至下(每层从左至右)进行1至n的编号,则对任一结点i(1in)有: (1)若i1,则i的双亲是i/2;若i=1,则i是根,无双亲。 (2)若2in,则i的左孩子是2i;否则, i无左孩子。 (3)若2i+1n,则i的右孩子是2i+1;否则, i无右孩子。,数据结构-第六章 树和二叉树,13,6.2.3 二叉树的基本操作,1)构造空二叉树 InitBiTree(&T) 2)销毁已存在的二叉树 DestroyBiTree(&T) 3)将二叉树清为空树 ClearB
9、iTree(&T) 4)求二叉树的深度 BiTreeDepth(T) 5)求二叉树的根 Root(T) 6)求指定结点的双亲 Parent(T, e) 7)求指定结点的左/右孩子 LeftChild(T, e)/ RightChild(T, e) 8)求指定结点的左/右兄弟 LeftSibling(T, e) / RightSibling(T, e) 9)插入子树c作为结点p的左/右子树 InsertChild(&T,&p,LR,c) 10)删除p结点的左/右子树 DeleteChild(&T,&p,LR) 11)遍历操作 TraverseBiTree(T, Visit(),数据结构-第六章
10、树和二叉树,14,6.2.4 二叉树的存储结构,6.2.4.1 顺序存储结构 按完全二叉树编号存放A B C - - D E0 1 2 3 6 13,typedef MAX_TREE_SIZE 100 /对应完全二叉树的最大结点编号 typedef TElemType SqBiTreeMAX_TREE_SIZE+1 ,bt,SqBiTree bt;,空间浪费可能大,特例:单枝树。,数据结构-第六章 树和二叉树,15,存储结点数据和左、右孩子在向量中的序号 0 1 2 3 4 data A B C D Elc 1 -1 3 -1 -1 rc 2 -1 -1 4 -1,#define MAX_SI
11、ZE 80 /预定义最大结点数目 typedef struct TElemType data ;int lc,rc ; int parent ; TNode; typedef TNode SBiTreeMAX_SIZE ;,bt,SBiTree bt;,data lc rc,数据结构-第六章 树和二叉树,16,6.2.4.2 链式存储结构,二叉链表,三叉链表/带双亲的二叉链表,lchild data rchild,lchild data parent rchild,A, B ,C , D, E ,A , B ,C , D, E ,bt,bt,数据结构-第六章 树和二叉树,17,二叉链表的类型定
12、义,三叉链表的类型定义,typedef struct BiTNode TElemType data;struct BiTNode *lchild, *rchild; BiTNode, * BiTree;,typedef struct TriTNode TElemType data;struct TriTNode *lchild, *rchild,*parent; TriTNode, * TriTree;,数据结构-第六章 树和二叉树,18,6.3 遍历二叉树,目的:非线性结构线性结构 6.3.1 概念指按某条搜索路线走遍二叉树的每个结点,使得树中每个结点都被访问一次,且仅被访问一次。,二叉树每
13、个结点有两个后继,则存在如何遍历即按什么样的搜索路径进行遍历的问题。必须找到一种规律,将二叉树转换为一种线性结构,然后进行遍历。,在实际应用中通常要在二叉树中查找具有某些特性的结点,或是对二叉树中的所有结点作某种处理,这就需要涉及二叉树的遍历问题。,数据结构-第六章 树和二叉树,19,6.3.2 典型的遍历方法 TraverseBiTree(T, Visit(),先(根)序遍历(DLR):PreOrderTraverse (T, Visit() 中(根)序遍历(LDR):InOrderTraverse (T, Visit() 后(根)序遍历(LRD):PostOrderTraverse (T,
14、 Visit() 层序遍历:LevelOrderTraverse (T, Visit(),上述逆序方式很少使用,T,对“二叉树”而言,可以有三条搜索路径: 先上后下的按层次遍历; 先左(子树)后右(子树)的遍历; 先右(子树)后左(子树)的遍历。,数据结构-第六章 树和二叉树,20,D L R,先序遍历序列:A B D C,先序遍历:,访问根结点、遍历左子树、遍历右子树,数据结构-第六章 树和二叉树,21,L D R,中序遍历序列:B D A C,中序遍历:,遍历左子树、访问根结点、遍历右子树,数据结构-第六章 树和二叉树,22,L R D,后序遍历序列: D B C A,后序遍历:,遍历左子
15、树、遍历右子树、访问根结点,数据结构-第六章 树和二叉树,23,确定遍历结果的一种简单方法,先序遍历(DLR):ABDEC 中序遍历(LDR):DBEAC 后序遍历(LRD):DEBCA利用遍历结果确定二叉树 先序序列+中序序列 中序序列+后序序列 先序序列+后序序列先序序列: ABCDEFGH中序序列: BDCEAFHG,A,B C,D E,A,B,F,C,G,D,E,H,思考:层序+先序/中序/后序能否确定?如何做?,数据结构-第六章 树和二叉树,24,6.3.3 二叉树遍历算法 (存储结构为二叉链表) 约定:遍历要求对每个数据元素调用函数 Visit。6.3.3.1 先序遍历算法,Sta
16、tus PreOrderTraverse(BiTree T, Status(* Visit)(TElemType e) if (T) if ( Visit(T-data) )if ( PreOrderTraverse(T-lchild, Visit) )if ( PreOrderTraverse(T-rchild, Visit) ) return OK;return ERROR;else return OK; /PreOrderTraverse,递归算法,数据结构-第六章 树和二叉树,25,Status pre(BiTree T) if (T) printf(T-data);pre(T-lch
17、ild);pre(T-rchild); ,返回,返回,返回,返回,A,C,B,D,返回,先序序列:A B D C,数据结构-第六章 树和二叉树,26,算法分析:,时间复杂度沿遍历路线访问每一个结点,对含n个结点的二叉树,时间复杂度为O(n)。空间复杂度递归算法所需辅助空间为树的深度,最坏情况下树的深度为h。空间复杂度O(h)。,数据结构-第六章 树和二叉树,27,改进的递归算法,Status PreOrderTraverse1(BiTree T) if (T) if ( visit(T-data) ) if (T-lchild) if ( ! PreOrderTraverse1(T-lchil
18、d) ) return ERROR;if (T-rchild)if (! PreOrderTraverse1(T-rchild) ) return ERROR;return OK;else return ERROR;else return OK; /PreOrderTraverse1,目的:减少对空二叉树的递归调用。,数据结构-第六章 树和二叉树,28,非递归算法,Status PreOrderTraverse2(BiTree T) IniStack(S);p=T ;Push(S, NULL);while ( p ) if ( !visit(p-data) ) return ERROR;if
19、( p-rchild ) Push(S, p-rchild); if ( p-lchild ) p=p-lchild; else Pop(S, p);return OK; /PreOrderTraverse2,数据结构-第六章 树和二叉树,29,6.3.3.2 中序遍历算法,Status InOrderTraverse(BiTree T, Status(* Visit)(TElemType e) if (T) if ( InOrderTraverse(T-lchild, Visit) )if ( Visit(T-data) )if ( InOrderTraverse(T-rchild,Visi
20、t) ) return OK;return ERROR;else return OK; /InOrderTraverse,递归算法,数据结构-第六章 树和二叉树,30,非递归算法一,Status InOrderTraverse1(BiTree T) InitStack(S);Push(S, T);while (! StackEmpty (S) while (GetTop(S,p) / InOrderTraverse1 P130-6.2,p1,p2,p3,NULL,NULL p3 p2 p1,数据结构-第六章 树和二叉树,31,非递归算法二,Status InOrderTranverse2(Bi
21、Tree T) InitStack(S); p = T; 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; return OK; / InOrderTraverse2 P131-6.3,两种方法的区别是(二)的处理要简洁些,空指针不需要入栈,而方法一有多次的空指针入栈、出栈操作。,LDR,p3 p2 p1,数据结构-第六章 树和二叉树,32,6.3.3.2 后序遍历算法,Status PostOrderT
22、raverse(BiTree T, Status(* Visit)(TElemType e) if (T) if ( PostOrderTraverse(T-lchild, Visit) )if ( PostOrderTraverse(T-rchild, Visit) )if ( Visit(T-data) ) return OK;return ERROR;else return OK; /PostOrderTraverse,递归算法,数据结构-第六章 树和二叉树,33,非递归算法,为了区分两次返回根结点时栈的不同处理方式,在堆栈中增加一个mark域:mark=0表示刚刚经过此结点;mark=
23、1表示左子树处理结束返回;mark=2表示右子树处理结束返回 每次根据栈顶元素的mark域值决定做何种动作。,数据结构-第六章 树和二叉树,34,Status PostOrderTraverse1(BiTree T) InitStack(S); Push (S,T,0); /根结点(指针、mark)入栈while(!StackEmpty(S) Pop(S,a);switch(a.mark) case 0: Push(S,a.p, 1); /修改mark域if(a.p-lchild) Push(S,a.p-lchild,0); /访问左子树break;case 1: Push(S,a.p,2);
24、 /修改mark域if(a.p-rchild) Push(S,a.p-rchild,0); /访问右子树break;case 2: if (!visit( a.p-data) return ERROR; return OK; /PostOrderTraverse1,数据结构-第六章 树和二叉树,35,6.3.3.4 按层次遍历的算法,Status LevelOrderTraverse(BiTree T, Status(* Visit)(TElemType e) if (T) InitQueue(Q);EnQueue(Q, T); /根结点的指针T入队列while ( ! QueueEmpty
25、(Q) ) DeQueue(Q, p); /从队头取一个指针if ( !Visit(p-data) ) return ERROR; if (p-lchild) EnQueue(Q, p-lchild);if (p-rchild) EnQueue(Q, p-rchild);return OK; /LevelOrderTraverse,A,数据结构-第六章 树和二叉树,36,6.3.4 二叉树递归遍历算法的利用 在二叉树中查找结点值为x的结点,BiTNode * PreFind(BiTree T, TElemType x) if ( !T ) return NULL;if (T-data=x) r
26、eturn T;else p=PreFind(T-lchild, x);if (p) return p;else return PreFind(T-rchild, x) ; /PreFind,(先序/中序/后序),数据结构-第六章 树和二叉树,37,求二叉树中每个结点所处的层次,Status PreLevel(BiTree T, int level) if (T) printf( bt-data, level);PreLevel(T-lchild, level+1);PreLevel(T-rchild, level+1);return OK /PreLevel调用语句:PreLevel (T,
27、 1),(先序),数据结构-第六章 树和二叉树,38,若二叉树为空树,则深度为0; 否则,二叉树的深度应为其左、右子树深度的最大值加1。 由此,需先分别求得左、右子树的深度。,求二叉树的高度,int PostHeight(BiTree T) if ( !T ) return 0;h1=PostHeight(T-lchild);h2=PostHeight(T-rchild);if (h1h2) return h1+1;else return h2+1; / PostHeight,方法1.分析二叉树的深度和它的左、右子树深度之间的关系。,(后序),数据结构-第六章 树和二叉树,39,求二叉树的高度
28、,方法2.分析二叉树的深度和结点的“层次”间的关系。,从二叉树深度的定义可知,二叉树的深度即为其叶子结点所在层次的最大值。由此,可通过遍历求得二叉树中所有结点的“层次”,从中取其最大值。,void Depth(BiTree T , int level, int / 调用之前 level 的初值为 1。 / dval 的初值为 0.,(先序),数据结构-第六章 树和二叉树,40,复制一棵二叉树,右子树,根元素,T,左子树,右子树,NEWT,左子树,右子树,左子树,右子树,方法1.(后序遍历),其基本操作为:生成一个结点,数据结构-第六章 树和二叉树,41,BiTNode *GetTreeNode
29、(TElemType item, BiTNode *lptr , BiTNode *rptr ) /生成一个二叉树的结点:其数据域为item, /左指针域为lptr,右指针域为rptr. if (!(T = (BiTNode *)malloc(sizeof(BiTNode)exit(OVERFLOW);T- data = item;T- lchild = lptr; T- rchild = rptr;return T; ,BiTNode *CopyTree(BiTNode *T) if (!T ) return NULL;if (T-lchild ) newlptr = CopyTree(T-
30、lchild); /复制左子树else newlptr = NULL;if (T-rchild ) newrptr = CopyTree(T-rchild); /复制右子树else newrptr = NULL;newT = GetTreeNode(T-data, newlptr, newrptr);return newT; / CopyTree,数据结构-第六章 树和二叉树,42,例如:下列二叉树的复制过程如下:,newT,数据结构-第六章 树和二叉树,43,复制一棵二叉树,Status PreCopy(BiTree T1, BiTree /PreCopy,方法2.(先序遍历),数据结构-第
31、六章 树和二叉树,44,6.4 线索二叉树,6.4.1 引入线索二叉树的意义利用二叉链表的空指针域,建立指向该结点的前驱/后继(某种遍历方式下)结点的指针,方便二叉树的线性化使用。(n个结点的二叉树含有n+1空指针域)6.4.2 线索二叉树的存储结构lchild LTag data RTag rchild左特征位 LTag= 0 lchild域指示结点的左孩子1 lchild域指示结点的前驱结点右特征位 RTag= 0 rchild域指示结点的右孩子1 rchild域指示结点的后继结点,T,数据结构-第六章 树和二叉树,45,例类型定义,先序线索二叉树 ABDCE,0 A 0,1 B 0,0
32、C 1,1 D 1,1 E 1 ,bt,typedef enum PointerTag Link,Thread;/ Link=0: 指针, Thread=1:线索 typedef struct BiThrNode TElemType data;Struct BiThrNode *lchild, *rchild;PointerTag LTag, RTag; BiThrNode, *BiThrTree;,数据结构-第六章 树和二叉树,46,6.4.3 线索二叉树的操作,6.4.3.1 查找结点p的前驱 /后继结点,(1)中序线索二叉树 p的后继若p-RTag=1,则p-rchild即为所求;若p-
33、RTag=0,则从其右子沿着左链走到LTag=1的那个结点就是。 p的前驱若p-LTag=1,则p-lchild即为所求;若p-LTag=0,则从其左子沿着右链走到RTag=1的那个结点就是。,p,L D R,LDR,LDR,数据结构-第六章 树和二叉树,47,(2)后序线索二叉树 p的前驱若p-LTag=1,则p-lchild即为所求;若p-LTag=0,则若p-RTag=0, 则p-rchild即为所求若p-RTag=1, 则p-lchild即为所求 p的后继与双亲结点有关,因二叉链表中没有指向双亲结点的指针,就可能需通过二叉树的后序遍历才可确定,因而后序线索二叉树在此问题上并不比普通二叉
34、树有效。 (3)先序线索二叉树 与后序线索二叉树对偶,p,中序线索二叉树可以方便地查找一个结点的前驱/后继结点。,L R D,LRD,数据结构-第六章 树和二叉树,48,6.4.3.2 二叉树中序线索化,带头结点的中序线索二叉树(双向线索链表),算法思想 利用中序遍历算法,算法步骤 若二叉树p非空,1) 左子树线索化;2) 访问当前结点p:2.1) 置 LTag和RTag;2.2) 建立p的前趋线索;2.3) 建立p的前趋pre的后继线索;2.4) 将p作为下一个被访问结点的前趋结点3) 右子树线索化;,算法描述参教材P134-135 算法6.6、6.7,数据结构-第六章 树和二叉树,49,算
35、法描述,Status InOrderThreading( BiThrTree / InOrderThreading P134-6.6,Void InThreading( BiThrTree p) if (p) InThreading(p-lchild);if ( ! p-lchild) p-LTag=Thread; p-lchild=pre; if ( ! pre-rchild) pre-RTag=Thread; pre-rchild=p; pre=p;InThreading(p-rchild); / InThreading P135-6.7,数据结构-第六章 树和二叉树,50,6.4.3.3
36、 中序线索二叉树的中序遍历,二叉线索树的遍历不需要递归,在先序/中序线索二叉树中,利用线索可方便地找到当前结点的后继结点。因此,对有 n个结点的二叉线索树,可以在 O(n) 时间内遍历完该树。算法步骤若二叉树p非空,1) 找到中序序列的第一个结点;2) 访问当前结点;3) 将当前结点的后继作为新的当前结点;4) 当前结点存在,则转2),数据结构-第六章 树和二叉树,51,算法描述,Status InOrderTraverse_Thr(BiThrTree T, Status (* Visit)(TElemType e) ) p=T-lchild;while ( p!=T ) while ( p-
37、LTag=Link) p=p-lchild;if ( !Visit(p-data) ) return ERROR;while (p-Rtag=Thread / InOrderTraverse_Thr P134-6.5,数据结构-第六章 树和二叉树,52,*6.4.3.4 线索二叉树的更新操作,问题点 在线索二叉树上插入或删除一棵子树或者结点时,指针和线索都需作相应的改动。解决方法 1)还原为普通二叉树,再重新将其线索化。 2)讨论可能出现的各种情况,寻找规律。,数据结构-第六章 树和二叉树,53,6.5 赫夫曼树及其应用,6.5.1 赫夫曼树(最优二叉树)的概念 例 编制将百分制转换为五级分制
38、的程序(10000数据)分数 059 6069 7079 8089 90100比例 0.05 0.15 0.40 0.30 0.10a60 a80 bad a70 a70 a90 pass a80 a60 general good excellentgeneral a90 bad passgood excellent,Y N,Y N,31500次,22000次,数据结构-第六章 树和二叉树,54,术语,树的路径长度 从树根到每一个结点的路径上的分支数。 带权路径长度 结点的路径长度与该结点的权之积。 树的带权路径长度 树中所有叶子结点的带权路径长度之和。最优二叉树(赫夫曼树) 带权路径长度WP
39、L最小的二叉树。 最优树 由给定的n 个带权叶子结点所构成的所有 m 叉树中,必存在一棵其带权路径长度取最小值的树,称为“最优树”。,数据结构-第六章 树和二叉树,55,例 有4个结点,权值分别为7,5,2,4,构造有4个 叶子结点的二叉树。,WPL=7*2+5*2+2*2+4*2=36,WPL=7*3+5*3+2*1+4*2=46,WPL=7*1+5*2+2*3+4*3=35,数据结构-第六章 树和二叉树,56,6.5.2 建立赫夫曼树(最优二叉树)的方法,基本思想使权大的结点靠近根。 (1)将给定权值从小到大排序成w1,w2,wm,生成一个森林F=T1,T2,Tm,其中Ti是一个带权Wi的
40、根结点,它的左右子树均空。 (2)把F中根的权值最小的两棵二叉树T1和T2合并成一棵新的二叉树T: T的左子树是T1 ,右子树是T2 ,T的根的权值是T1、T2树根结点权值之和。 (3)将T按权值大小加入F中,同时从F中删去T1和T2 (4)重复(2)和(3),直到F中只含一棵树为止,该树即为所求。,数据结构-第六章 树和二叉树,57,例 w=5, 29, 7, 8, 14, 23, 3, 11,数据结构-第六章 树和二叉树,58,例 ,第一步:第二步:第三步:,0.05,0.10,0.15,0.30,0.40,0.05,0.10,0.15,0.15,0.30,0.40,0.05,0.10,0
41、.15,0.30,0.30,0.40,0.40,0.30,0.60,0.15,0.10,0.05,数据结构-第六章 树和二叉树,59,第四步:,0.40,0.30,0.15,0.05,0.10,1.00,(a60),(60a70),(80a90),(70 a80),20500次 (10000数据),数据结构-第六章 树和二叉树,60,赫夫曼树的存储结构,初始有n个叶子结点,则构造出的赫夫曼树(属于严格二叉树)共有2n-1个结点。 用大小为2n的向量存储赫夫曼树顺序存储。0 1 2 n n+1 2n-1weightlchild 0 0 0rchild 0 0 0parent注:用0表示空指针,i
42、 j,typedef struct unsigned int weight;unsigned int parent, lchild, rchild; HTNode, *HuffmanTree; /动态分配存储空间,数据结构-第六章 树和二叉树,61,构造赫夫曼树的算法步骤,1)申请空间并初始化HT12n-1:weight=0, lchild=rchild=parent=0 2)置HT1n的weight值 3)进行以下n-1次合并,依次产生HTi,i=n+12n-1:3.1)在HT1i-1中选两个未被选过的weight最小的两个结 点HTs1和HTs2 (从parent = 0 的结点中选)3.
43、2)修改HTs1和HTs2的parent值: parent=i3.3)置HTi:weight=HTs1.weight + HTs2.weight ,lchild=s1, rchild=s2,HuffmanTree HT;,数据结构-第六章 树和二叉树,62,构造赫夫曼树的算法,Void BuildHT(HuffmanTree /BuildHT,数据结构-第六章 树和二叉树,63,6.5.3 赫夫曼树的应用,最佳判定树 赫夫曼编码:用于通信和数据传送中字符的二进制编码,可以使电文编码总长度最短。 例 对time tries truth赫夫曼编码字符集 D=t, i, m, e, r, s, u,
44、 h出现频次 W=4, 2, 1, 2, 2, 1, 1, 1,u,h,t,i,e,r,m,s,0,1,0,1,0,1,0,1,0,1,0,1,0,1,t 01 i 100 m 1110 e 101 r 110 s 1111 u 000 h 001,数据结构-第六章 树和二叉树,64,赫夫曼编码是不等长编码 赫夫曼编码是前缀编码,即任一字符的编码都不是另一字符编码的前缀 赫夫曼编码树中没有度为1的结点。若叶子结点的个数为n,则赫夫曼编码树的结点总数为 2n-1 发送过程:根据由赫夫曼树得到的编码表送出字符数据 接收过程:按左0、右1的规定,从根结点走到一个叶结点,完成一个字符的译码。反复此过程,直到接收数据结束,数据结构-第六章 树和二叉树,65,赫夫曼编码表的存储结构,0 1 2n,HC,typedef char * * HuffmanCode;,HuffmanCode HC;,1 0 1 1,0 1 0,1 0 0 1 0 1,