文章目录
- 二叉树的应用__哈夫曼树和哈夫曼编码v1
- 相关概念
- 权(结点的权)Weight:
- 结点权
- 边权
- 其他概念
- 哈夫曼Huffman相关问题
- huffman树
- 最优2叉树
- 最优m叉树
- 手工排序推荐使用:
- 构造哈夫曼树
- 构造算法
- 适合手动计算
- 哈夫曼树的不唯一性
- haffman编码
- 固定长度编码:
- 可变长度编码:
- 前缀编码
- 哈夫曼编码
- 例(huffman编码和huffman树)
- 正确实例(其中的一种)
- 错误实例
- 并查集(UnionSet)
- 应用
二叉树的应用__哈夫曼树和哈夫曼编码v1
相关概念
权(结点的权)Weight:
结点权
- 树中的结点被赋予一个(表示某种意义的)数值,这个数值成为结点的权
- 结点权有时标注在通往目标结点的边上
- 注意,结点权是属于结点的
边权
- 和结点权类似,权是属于边的
- 在AOE网中,就是这类情况
其他概念
- 路径Path:
- 从树中的一个结点到另一个结点之间的分支构成这两个结点间的路径
- 路径长度L(node):
- 路径Path经过的边数(分支数)(而不是结点数来计量)
- 树的路径长度TL(TreeLength)
- 指从树根到每一个结点(目标结点)的路径长度之和
- 结点的带权路径长度WPL(node):
- 路径长度和该(路径目标)结点的权值的乘积
- 树的带权路径长度WPL(LeaveNodes):
- 树中所有叶子结点的带权路径长度之和WPL(Tree)
- 设树T有n个叶子结点
- 记为WPL(Weighted Path Length ((of Tree))
- 建议:求和式中,总是先写权,再写路径长
哈夫曼Huffman相关问题
huffman树
最优2叉树
- 在(所有)含有n个叶子结点的二叉树中,其中带权路径长度WPL(BT)最小二叉树称为哈夫曼树(最优二叉树)
- 没有度为1的结点(仅有0度和1度结点)
- 是一棵严格的2叉树
最优m叉树
- 是一棵严格的m叉树
- 基本原则就是从最小的m个结点开始构造更高的树(森林)
- 特点是:结点权值大的比较靠近根结点
- 结点权值小的,离根结点路径更长
- 给定序列2,3,6,9,12,17,18,24,30
- 以它们为叶子结点构建的最优3叉树如下(同一结点的子树交换位置不影响最优性)
- 但总是具有最短路径长度:
手工排序推荐使用:
- 插入排序
- 快速排序结合插入排序效果合适极好的
- 先扫描一眼待排序列中的大概的中位数
- 借助插入插入独立分区
- 归并排序
构造哈夫曼树
- 从huffman树的定义直观的感受到,要使得wpl(Tree)最小,应该让最长的路径和最小的权相乘
- 采取的是**自底向上**的方式进行构造
- 小(权值)的结点靠下
- 大权值的结点靠上
- 手工计算前,不妨排个序(尽管排序不是算法所要求的,但是有利于手工计算降低错误率!)
- 在构造哈夫曼树的过程中,需要动态的维护这个序列
- 首先,将n个权值分别为
的结点视为单结点二叉树,并成为w序列
- n描述的是w序列的长度,随着构造算法的进行,n会递减至0,直到算法结束
- 简单理解就是n当前的序列长度(变量),而不是一个固定值
构造算法
- 构造新树
(根结点)
- 从w序列中选出两棵树
- 具有第一小(最小)根结点权值的树
(其值记为k1),作为新构造树v的左子树
标上编码1
- 类似的第二小(次小)者
,其值记为k2,作为v的右子树
标上编码0
左子树和右子树顺序可以互换;而且0/1也可以互换(但是建议坚持左0右1或者左1右0中的一种)
实际上是只要确保左右子树一个是1一个是0即可(能够区分前缀)
- 置v=k1+k2
- 随后把w序列中的k1,k2两棵树删除
- 同时把新构造的树
加入(插入)到w序列中(的合适位置)
- (至此,序列长度n减去1)
- 在手工计算的时候,可以采用插入排序的手法(扑克牌手法),将新树v插入到合适的位置中,方便循环的执行
- 注意到v在循环中是不断迭代的.
- 重复执行上述3大步骤,直到w序列为空
适合手动计算
- 构造新树
(根结点)
- 从拍好序的有序序列w中选出两棵树(如果是降序排列,选出末尾两棵,否则头两棵)
- 具有第一小(最小)根结点权值的树
(其值记为k1),作为新构造树v的左子树
- 从w序列中删除(划掉)
- 类似的第二小(次小)者
,其值记为k2,作为v的右子树
- 从w序列中删除(划掉)
- 置v=k1+k2
- 同时把新构造的树
加入(插入)到w序列中(的合适位置,以维护w序列有序)
- 重复执行上述2大步骤,直到w序列为空
哈夫曼树的不唯一性
- 上述的算法中,左子树和右子树顺序可以互换,这可能产生不同的haffman树
- 但是这些树的wpl都是相等且最优的
- wpl和0/1的标注没有关系,0/1的标准方向和具体的编码有关系
haffman编码
固定长度编码:
- 对每个字符用相长度的二进制位表示,则这种编码方式(表示方式)就是固定长度编码
可变长度编码:
- 对于允许不同字符使用不等长度的二进制位表示,这种编码方式就是可变长度编码
- 对频率高的字符赋以短编码;对频率低的字符赋以长一点的编码
- 这使得字符的平均编码可以比固定长度编码的方式的平均长度要短,达到压缩数据的效果
前缀编码
- 在编码集合中,如果任何一个编码不会是其他编码的前缀,则这样的编码方式是前缀编码
- 其特点是解码简单
- 例如,编码表
A | 0 |
B | 101 |
C | 100 |
- 00101100这个编码串只能够被解码成AABC,而不可能有其他的解释
- 另一方面,
A | 0 |
B | 101 |
C | 100 |
D | 00 |
- 00101100此时可以被解释为:
- AABC
- DBC
- 也就是不唯一的
哈夫曼编码
- huffman编码是一种常用的数据压缩编码
- 可以从huffmanTree得到huffmanCoding
- huffmanTree的构造材料是一些值(或者说带有权值的结点)
例(huffman编码和huffman树)
- 假设我们要对字符串S进行编码,s中的元素包含{a,b,c,d,e,f}它们的出现频率数据如下:
字符 | 出现的频度(作为huffman叶子结点的权值) |
a | 45 |
b | 13 |
c | 12 |
d | 16 |
e | 9 |
f` | 5 |
- 排序
字符 | 出现的频度(作为huffman叶子结点的权值) |
a | 45 |
d | 16 |
b | 13 |
c | 12 |
e | 9 |
f` | 5 |
- 根据上面的材料表,可以构造haffman树
正确实例(其中的一种)
- a:1
- b:010
- c:011
- d:000
- e:0010
- f:0011
- 上面的哈夫曼树坚持同一棵子树的孩子左小右大
- 可以看到,高频的a的编码最短,低频的f,e编码最长!
错误实例
并查集(UnionSet)
- 并查集的是相对简单的一种数据结构,但是最好是知道它的引用场景,以便更好的认识它
应用
- 可以利用并查集完成kruskal 最小生成树算法
- 判断一条边是否加入前,先查找这条边关联的两个顶点是否属于同一个集合
- 即判断加入这条边后是否会形成回路
- 如果会形成回路,则继续判断下一条边
- 否则将该边和对应的顶点加入最小生成树T
- 循环上述步骤,知道所有顶点加入到T