180 likes | 314 Views
9.5.3 哈夫曼树. 树的带权路径长度 ( Weighted Path Length, WPL) 所有 叶子 结点的带权路径长度之和为 树的带权路径长度 ,记为: wpl= 其中: n 为叶子结点数目, w i 为第 i 个叶子结点的权值, l i 为第 i 个叶子结点的路径长度。. a. b. c. d. 7. 5. 2. 4. c. 2. 7. a. 4. d. 5. b. a. b. 2. 4. c. d. 7. 5. 举例 :
E N D
9.5.3 哈夫曼树 树的带权路径长度(Weighted Path Length, WPL) • 所有叶子结点的带权路径长度之和为树的带权路径长度,记为: wpl= • 其中: • n为叶子结点数目, • wi为第i 个叶子结点的权值, • li为第i 个叶子结点的路径长度。
a b c d 7 5 2 4 c 2 7 a 4 d 5 b a b 2 4 c d 7 5 举例 : 有4个结点,权值分别为7,5,2,4,构造有4个叶子结点的二叉树 WPL=(7+5+2+4)*2=36 WPL=(7+5)*3+2*1+4*2=46 WPL=7*1+5*2+(2+4)*3=35 带权路径长度达到最小
7 a 5 b 2 4 c d 二、构造哈夫曼树 1.哈夫曼树的定义 设有n个带权的叶子结点,想构造一棵二叉树,使WPL(带权路径长度)为最小,该树称为最优二叉树,也称为哈夫曼树(Huffman tree)。 • 特点: • 权值大的叶子离根最近; • 只有0度和2度结点 • 若叶子有n个,则: 总结点数=2n-1
2.哈夫曼树的构造 假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1,w2,…,wn,则Huffman树的构造算法: (1) 将w1,w2,…,wn看成是有n 棵(仅有根结点的)二叉树的森林; (2) 在森林中选出两个根的权值最小的和次小的二叉树,作为新二叉树的左、右子树,且新二叉树的根权值为其左、右子树根权值之和;约定:权值小者为左子树,权值大者为右子树 (3)从森林中删除选取的两棵二叉树,并将新二叉树加入森林; (4)重复(2)、(3)步,直到森林中只剩一棵二叉树为止,该树即为所求的哈夫曼树。
举例:已知叶子结点{A,B,C,D}的权值序列为{1,5,7,3},则构造Huffman树过程如下:举例:已知叶子结点{A,B,C,D}的权值序列为{1,5,7,3},则构造Huffman树过程如下: 5 7 1 5 7 3 4 4 4 初始的森林 1 1 1 3 3 3 1与3合并 16 7 9 9 7 5 5 4与5合并 7与9合并 WPL =7*1+5*2+(1+3)*3 =29 Huffman树构建练习: 权值序列为 {9,8,4,6,7}
三、哈夫曼编码——哈夫曼树的应用 在远程通讯中,要将待传字符转换成由二进制组成的字符串: 设要传送的字符为: ABACCDA 若编码为: A—00 B—01 C—10 D---11 00010010101100 若将编码设计为长度不等的二进制编码,即让待传字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。
设要传送的字符为:ABACCDA 若编码为: A—0 B—00 C—1 D---01 ABACCDA 但: 0000 AAAA ABA BB 重码 000011010 关键:要设计长度不等的编码,则必须使任一字符的编码都不是另一个字符的编码的前缀。这种编码称作前缀编码。
1 0 A 1 0 C 0 1 B D 设要传送的字符为: ABACCDA 若编码为 :A—0 B—110 C—10 D---111 0110010101110 规定: 左分支用“0”表示 右分支用“1”表示 采用Huffman树设计二进制前缀编码
1 0 A 1 0 C 0 1 B D 译码过程:分解接收字符串:遇“0”向左,遇“1”向右;一旦到达叶子结点,则译出一个字符,反复由根出发,直到译码完成。 0110010101110 ABACCDA
14 8 6 4 3 3 4 A T B 2 2 C S 例:已知某系统在通讯时,只出现C,A,S,T,B五种字符,它们出现的频率依次为2,4,2,3,3,试设计Huffman编码。 • 先构建Huffman 树 字符{C, A, S, T, B}对应的权值为:{2,4,2,3,3} 构造的Huffman树
14 1 0 8 6 0 1 1 0 4 3 3 4 0 1 A T B 2 2 C S • 由构建的Huffman树得出HUffman编码: • 约定:左分支用“0”表示;右分支用“1”表示 Huffman编码为: T—00 B —01 A —10 C —110 S —111 出现频率越大的字符,其Huffman编码越短。
若左子树非空,左指针不变;若左子树为空,则左指针指向某种遍历顺序下该结点的前驱;若右子树非空,右指针不变,仍指向右子女;若右子树为空,则右指针指向某种遍历顺序下该结点的后继。如果规定遍历顺序为中序,则称为中序穿线二叉树;若左子树非空,左指针不变;若左子树为空,则左指针指向某种遍历顺序下该结点的前驱;若右子树非空,右指针不变,仍指向右子女;若右子树为空,则右指针指向某种遍历顺序下该结点的后继。如果规定遍历顺序为中序,则称为中序穿线二叉树; 7.6 穿线二叉树 7.6.1 穿线二叉树的定义
在穿线二叉树的每个结点中,增加两个标志位:ltag和rtag,其含义为:ltag=0 表示结点的左指针指向其左子女;ltag=1 表示结点的左指针指向其中序遍历的前驱;rtag=0 表示结点的右指针指向其右子女;rtag=1 表示结点的右指针指向其中序遍历的后继。 ltag lchild data rchild rtag
a a b b e e c d g c d g f f (a)一棵二叉树 (b)中序穿线二叉树
1、中序穿线二叉树在链接方式下的数据类型定义typedef char datatype; typedef struct node { datatype data; int ltag,rtag; /*左、右标志位*/ struct node *lchild,*rchild; } binthrnode, *binthrtree; 7.6.3 中序穿线二叉树的存储结构及其实现
3、 中序遍历中序穿线二叉树inthrtree(p)基本思想:首先找到中序遍历下的第一个结点( “最左下”的结点),访问它后,然后不断寻找结点在中序下的后继结点并输出,直至所有的结点均被输出为止。 该点即是 p binthrtree insuccnode(binthrtree p) { /*寻找结点p在中序遍历下的后继结点*/binthrtree q; if (p->rtag==1) return p->rchild; else{ q=p->rchild; while (q->ltag==0) q=q->lchild; return q; } }
void inthrtree(binthrtree p) { /*中序遍历中序穿线二叉树p*/ if (p) { /*求二叉树p中序遍历下的第一个结点*/ while (p->ltag==0) p=p->lchild; /*依次遍历各结点*/ do { printf(“%c ”,p->data);p=insuccnode(p); } while (p); } }