dataStructrue_二叉树的应用__Huffman/哈夫曼树和哈夫曼编码/最优归并树/最优m叉树/UnionSet
文章目录
二叉树的应用__哈夫曼树和哈夫曼编码v1
相关概念
权(结点的权)Weight:
结点权
- 树中的结点被赋予一个(表示某种意义的)数值,这个数值成为结点的权
- 结点权有时标注在通往目标结点的边上
- 注意,结点权是属于结点的
边权
- 和结点权类似,权是属于边的
- 在AOE网中,就是这类情况
其他概念
-
路径Path:
- 从树中的一个结点到另一个结点之间的分支构成这两个结点间的路径
-
路径长度L(node):
- 路径Path经过的边数(分支数)(而不是结点数来计量)
-
树的路径长度TL(TreeLength)
- 指从树根到每一个结点(目标结点)的路径长度之和
-
结点的带权路径长度WPL(node):
- 路径长度和该(路径目标)结点的权值的乘积
-
树的带权路径长度WPL(LeaveNodes):
- 树中所有叶子结点的带权路径长度之和WPL(Tree)
- 设树T有n个叶子结点
- 记为WPL(Weighted Path Length ((of Tree))
- 记第 i 个带权叶结点的权值为 w i 记第i个带权叶结点的权值为w_i 记第i个带权叶结点的权值为wi
- L i 为根结点到第 i 个叶结点的路径长度 L_i为根结点到第i个叶结点的路径长度 Li为根结点到第i个叶结点的路径长度
-
w
p
l
=
∑
i
=
1
n
w
i
L
i
wpl=\sum\limits_{i=1}^{n}{w_i}{L_i}
wpl=i=1∑nwiLi
- 建议:求和式中,总是先写权,再写路径长
- 树中所有叶子结点的带权路径长度之和WPL(Tree)
哈夫曼Huffman相关问题
huffman树
最优2叉树
- 在(所有)含有n个叶子结点的二叉树中,其中带权路径长度WPL(BT)最小二叉树称为哈夫曼树(最优二叉树)
- 没有度为1的结点(仅有0度和1度结点)
- 是一棵严格的2叉树
最优m叉树
-
是一棵严格的m叉树
- 基本原则就是从最小的m个结点开始构造更高的树(森林)
- 特点是:结点权值大的比较靠近根结点
- 结点权值小的,离根结点路径更长
-
给定序列2,3,6,9,12,17,18,24,30
-
以它们为叶子结点构建的最优3叉树如下(同一结点的子树交换位置不影响最优性)
-
但总是具有最短路径长度:
- W P L = ( 2 + 3 + 6 ) ⋅ 3 + ( 9 + 12 + 17 + 18 + 24 ) ⋅ 2 + 30 ⋅ 1 = 11 ⋅ 3 + 80 ⋅ 2 + 30 ⋅ 1 = 223 \begin{aligned}WPL=&(2+3+6)\cdot3+(9+12+17+18+24)\cdot 2+30\cdot1 \\=&11\cdot3+80\cdot 2+30\cdot1 \\=&223 \end{aligned} WPL===(2+3+6)⋅3+(9+12+17+18+24)⋅2+30⋅111⋅3+80⋅2+30⋅1223
-
-
手工排序推荐使用:
- 插入排序
- 快速排序结合插入排序效果合适极好的
- 先扫描一眼待排序列中的大概的中位数
- 借助插入插入独立分区
- 归并排序
构造哈夫曼树
-
从huffman树的定义直观的感受到,要使得wpl(Tree)最小,应该让最长的路径和最小的权相乘
-
采取的是**自底向上**的方式进行构造
-
小(权值)的结点靠下
-
大权值的结点靠上
-
-
手工计算前,不妨排个序(尽管排序不是算法所要求的,但是有利于手工计算降低错误率!)
- 在构造哈夫曼树的过程中,需要动态的维护这个序列
-
首先,将n个权值分别为 w 1 , w 2 , w 3 , ⋯ , w n w_1,w_2,w_3,\cdots,w_n w1,w2,w3,⋯,wn的结点视为单结点二叉树,并成为w序列
- n描述的是w序列的长度,随着构造算法的进行,n会递减至0,直到算法结束
- 简单理解就是n当前的序列长度(变量),而不是一个固定值
构造算法
-
构造新树 v = w n + 1 v=w_{n+1} v=wn+1(根结点)
-
从w序列中选出两棵树
-
具有第一小(最小)根结点权值的树 w m i n 1 w_{min1} wmin1(其值记为k1),作为新构造树v的左子树
- 为有向边 v w m i n 1 为有向边vw_{min1} 为有向边vwmin1标上编码1
-
类似的第二小(次小)者 w m i n 2 w_{min2} wmin2,其值记为k2,作为v的右子树
- 为有向边 v w m i n 2 为有向边vw_{min2} 为有向边vwmin2标上编码0
-
左子树和右子树顺序可以互换;而且0/1也可以互换(但是建议坚持左0右1或者左1右0中的一种)
实际上是只要确保左右子树一个是1一个是0即可(能够区分前缀)
-
-
置v=k1+k2
-
随后把w序列中的k1,k2两棵树删除
- 同时把新构造的树
v
=
w
n
+
1
v=w_{n+1}
v=wn+1加入(插入)到w序列中(的合适位置)
- (至此,序列长度n减去1)
- 在手工计算的时候,可以采用插入排序的手法(扑克牌手法),将新树v插入到合适的位置中,方便循环的执行
- 注意到v在循环中是不断迭代的.
- 同时把新构造的树
v
=
w
n
+
1
v=w_{n+1}
v=wn+1加入(插入)到w序列中(的合适位置)
-
-
重复执行上述3大步骤,直到w序列为空
适合手动计算
- 构造新树
v
=
w
n
+
1
v=w_{n+1}
v=wn+1(根结点)
- 从拍好序的有序序列w中选出两棵树(如果是降序排列,选出末尾两棵,否则头两棵)
- 具有第一小(最小)根结点权值的树
w
m
i
n
1
w_{min1}
wmin1(其值记为k1),作为新构造树v的左子树
- 从w序列中删除(划掉)
- 类似的第二小(次小)者
w
m
i
n
2
w_{min2}
wmin2,其值记为k2,作为v的右子树
- 从w序列中删除(划掉)
- 具有第一小(最小)根结点权值的树
w
m
i
n
1
w_{min1}
wmin1(其值记为k1),作为新构造树v的左子树
- 置v=k1+k2
- 同时把新构造的树 v = w n + 1 v=w_{n+1} v=wn+1加入(插入)到w序列中(的合适位置,以维护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
- 也就是不唯一的
- 00101100此时可以被解释为:
-
-
哈夫曼编码
-
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
-
上面的哈夫曼树坚持同一棵子树的孩子左小右大
- w p l = 45 ∗ 1 + ( 12 + 13 + 16 ) ∗ 3 + ( 5 + 9 ) ∗ 4 = 224 wpl=45*1+(12+13+16)*3+(5+9)*4=224 wpl=45∗1+(12+13+16)∗3+(5+9)∗4=224
-
可以看到,高频的a的编码最短,低频的f,e编码最长!
错误实例
并查集(UnionSet)
- 并查集的是相对简单的一种数据结构,但是最好是知道它的引用场景,以便更好的认识它
应用
- 可以利用并查集完成kruskal 最小生成树算法
- 判断一条边是否加入前,先查找这条边关联的两个顶点是否属于同一个集合
- 即判断加入这条边后是否会形成回路
- 如果会形成回路,则继续判断下一条边
- 否则将该边和对应的顶点加入最小生成树T
- 循环上述步骤,知道所有顶点加入到T
- 判断一条边是否加入前,先查找这条边关联的两个顶点是否属于同一个集合
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了