本文共 4543 字,大约阅读时间需要 15 分钟。
前言
这是我听老师讲课做的笔记。
作者:RodmaChen 关注我的csdn博客,更多数据结构与算法知识还在更新
赫夫曼树及其应用(必考)
一. 定义
-
路径:从树中一个结点到另一个结点之间的分支构成两个结点之间
的路径。 -
路径长度:路径的上分支的数目。就是从一个结点到另一个结点有多少个路径
-
结点的路径长度:从根到该结点的路径长度。
-
树的路径长度:从树根到每一个结点的路径长度之和。
-
结点的权:在一些应用中,赋予树中结点的一个有某种意义实数
-
结点的带权路径长度:从根结点到各个叶结点的路径长度与相应结点权值的乘积。
-
树的带权路径长度:所有叶结点的带权路径长度之和,记作:
W P L = ∑ k = 1 n W k l k ( 其 中 , n : 叶 结 点 数 目 ; W k : 叶 结 点 k 的 权 值 ; l k 根 到 k 之 间 的 路 径 长 度 ) {WPL= \sum_{k=1}^n W_kl_k} (其中,n:叶结点数目;W_k:叶结点 k的权值;l_k根到k之间的路径长度) WPL=k=1∑nWklk(其中,n:叶结点数目;Wk:叶结点k的权值;lk根到k之间的路径长度) -
最优二叉树/ 赫 夫曼树: 假设有n个权值(w1,w2…wn),试
构造一棵有n个叶子结点的二叉树,每个叶子结点带权为wi , 则其中带权路径长度WPL最小的二叉树称作最优二叉树或赫 夫曼树。
二. 赫夫曼算法
1.基本思想
给定n个权值,先找出两个最小的权值
wi,wj,求wi+wj=wk
,把wk
与其它权值继续重复此动作,直至所有的wi
都成为叶子结点。
如下图所示:给定7,5,3,1
。选取根结点的权值最小(1)和次小(3)的两棵二叉树作为新的二叉树的左右子树构造新的二叉树,新的二叉树的根结点权值为左右子树根结点权值之和(4)。最后7,5,3,1
变成叶子结点。这样一颗树就是赫夫曼树。
2.算法的实现
(1)赫夫曼树的存储结构:
赫夫曼树是一种二叉树,由于赫夫曼树中没有度为 1
的结点,因此一棵有 n
个叶子的赫夫曼树共有 2n-1
个结点,可以用一个大小为 2n-1
的一维数组存放赫夫曼树的各个结点。
为什么是2n-1: [根据二叉树的性质三](file:///D:/课本笔记/我的笔记/计算机数据结构与算法笔记/第六章:树和二叉树的性质和定义.html)由于没有度为1的结点,所以度为2的结点N=n-1,总结点等于2n-1
由于每个结点同时还包含其双亲信息和孩子结点的信息,所以构成
一个静态三叉链表。 权值 双亲 左孩子 右孩子
(2)赫夫曼树的类型定义:
用静态三叉链表实现的哈夫曼树类型定义如下:
#define N 20 /* 叶子结点的个数。*/#define M 2*N-1 /* 所有结点的个数。*/typedef struct{ int weight ; /* 结点的权值*/ int parent ; /* 双亲的下标*/ int LChild ; /* 左孩子结点的下标*/ int RChild ; /* 右孩子结点的下标*/} HTNode, HuffmanTree[M+1];/* HuffmanTree 是一个结构数组类型,0 号单元不用。 */void CrtHuffmanTree(HuffmanTree ht, int w[ ], int n){ /*构造哈夫曼树 ht[M+1], w[ ]存放 n 个权值。*/ /* 1 ~ n 号单元存放叶子结点,初始化*/ for(i=1;i<=n;i++) ht[i] ={ w[i],0,0,0}; m=2*n-1; for(i=n+1;i<=m;i++) ht[i] ={ 0,0,0,0}; /* n+1 ~ m 号单元存放非叶结点,初始化 */ ——————————初始化完毕!对应算法步骤 1,下面左边的表—————— for(i=n+1; i<=m; i++) /*创建非叶结点,建哈夫曼树*/ { select(ht, i-1, s1, s2); /* 在 ht[1] ~ ht[i-1] 的范围内选择两 个 parent 为 0 且 weight 最小的结点,其序号分别赋值给 s1、s2 返回 */ ht [i].weight= ht [s1].weight+ ht [s2].weight; ht [s1].parent=i; ht [s2].parent=i; ht [i].LChild=s1; ht [i].RChild=s2; } /*哈夫曼树建立完毕*/}
(3)代码事列:
数据传送中的二进制编码。要传送数据
state, seat,act, tea, cat, set, a,eat
,如何使传送的长度最短?为了保证长度最短,先计算各个字符出现次数,将出现次数当作权值,如下表所示。
按照创建哈夫曼树算法,该例建立哈夫曼树的结果如下表所示:
三.赫夫曼编码
1.编码和解码(考例题)
(1)数据压缩过程称为编码。即将文件中的每个字符均转
换为一个惟一的二进制位串。(2)数据解压过程称为解码。即将二进制位串转换为对应
的字符。等长编码:每个字符的码长一样。
变长编码:频度高的(出现多的)字符编码短,反之则长。
举例: 假设需传送的电文为’ABACCDA'
,分别按定长和不定长编码,如
如上表所示:A、B、C、D的定长编码分别为00、01、10和11
,则上
’00010010101100’
,总长14位,对方接收时,可 按二位一分进行译码。 如果设计A、B、C、D的编码分别为0、00、1和01
,则上述7个字符的
000011010'
。但是,这样的电文无法翻 译,例如传送过去的字符串中前4个字符的子串’0000’就可有多种译法, 或是’AAAA'
,或是’ABA’
,也可以是’BB’
等。 前面两种编码一个太长,一个容易出错
前缀编码:若要设计长短不等的编码,则必须是任一个字符的编码
都不是另一个字符的编码的前缀
举例:可以利用二叉树来设计二进制的前缀编码。左走0右走1
二进制前缀 编码便称为赫夫曼编码。
2.编码实现
由于赫夫曼树中没有度为1的结点,则一棵有
共有n
个叶子结点的赫夫曼树2n-1
个结点,可以存储在一个大小为2n-1
的一维数组中。如何选定结点结构?由于在构成赫夫曼树之后,为求编码需从叶子结点出发走一条从叶子到根的路径;而为译码需从根出发走一条从根到叶子的路径。则对每个结点而言,既需知双亲的信息,又需知孩子结点的信息。
由此,设定下述存储结构:
//一一一一赫夫曼树和赫夫曼编码的存储表示----------typedef struct{ unsigned int weight;unsigned int parent, lchild, rchild;}EINode, * HuffmanTree: //动态分配数组存储赫夫曼树typedef char * *HuffmanCode; //动态分配数组存储赫夫曼编码表void HaffmanCoding(HuffmanTree &HT, HuffmanCode &HC,int *w,int n) { // w存放n个字符的权值(均>0),构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC /---构造赫夫曼树HT--- if (n<= 1) return;//如果只有一个结点,就没必要构造了m = 2 * n - 1; //结点总数HT = (HuffmanTree)malloc((m+ 1) * sizeof(HTNode)); //0号单元未用for (p=HT, i=1; i<=n; ++i, ++p, ++w) *p = { *w,0, 0,0 );for(; i≤=m; ++i,++p) *p ={ 0,0,0.0};for (i=n+ 1; i<=m; ++i) { //建赫夫曼树//在HT[1..i-1]选择parent 为0且weight 最小的两个结点,其序号分别为s1s2.Select(HT, i- 1, sl, s2);HT[sl]. parent = i; HT[s2]. parent = i;HT[i]. Ichild = s1; HT[i].rchild = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;}/---从叶子到根逆向求每个字符的赫夫曼编码---HC= (HuffmanCode)malloc ((n+1)*sizeof(char *)); //分配n个字符编码的头指针向量cd = (char * )malloc(n* sizeof(char)); //分配求编码的工作空间cd[n-1] = "\0"; //编码结束符。for(i=1; i<=n; ++i) { //逐个字符求赫夫曼编码start = n-1; //编码结束符位置for (c=i, f= HT[i]. parent; f!=0; :c= f, f=HT[f].parent) //从叶子到根逆向求编码if (HT[f].lchild==c) cd[--start] = "0”;else cd[--start] ="1";HC[i] = (char *)malloc((n-start)*sizeof(char)); // 为第i个字符编码分配空间strcpy(HC[i], &cd[start]); //从cd复制编码(串)到HC}free(ed); //释放工作空间}//HuffanCoding
临时存储编码(倒着存)
四.边学边练
(1)分析这三棵树路径长度和带权路径长度那个最小有什么特点。
(a):WPL=72+52+22+42=36 树的路径长度:10
(b):WPL= 73+53+21+42=46 树的路径长度:12
©: WPL= 71+52+23+43=35 树的路径长度:12
(2) 给定8个权值:5,29,7,8,14,23,3,11,构造赫夫曼树
答案:
例6-2 已知某系统在通信联络中只可能出现8种字符,其概率分别为0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11
,试设计赫夫曼编码。
解答:设权W=(5,29,7,8,14,23,3,11),n=8,则m=15,按上述算法可构造-棵赫夫曼树如图6.26所示。其存储结构HT的初始状态如图6.27(a)所示,其终结状态如图6.276)所示,所得赫夫曼编码如图6.27©所示。
作者:RodmaChen
本人博客: 本人b站求关注: 转载说明:跟我说明,务必注明来源,附带本人博客连接。
请给我点个赞鼓励我吧
转载地址:https://chenyunzhi.blog.csdn.net/article/details/106715285 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!