1 赫夫曼树定义及其原理
我们先把这两棵二叉树简化成 叶子结点带权的二叉树, 如下图所示
A表示不及格, B表示及格, C表示中等, D表示良好, E表示优秀。
从树中一个结点到另一个结点之间的分支构成两个结点之间的路径, 路径上的分支数目称为路径长度,
二叉树a中, 从根节点到D的路径长度为4, 二叉树b中, 从根节点到D的路径长度为2, 数的路径长度就是从树根 到每一个结点的路径长度之和, 二叉树 a的树路径长度为 1 + 1 +2+2+3+3+4+4 = 20
二叉树 b的树路径长度就是 1+2+3+3+2+1+2+2 = 16
如果考虑到带权的结点, 结点的带权的路径长度为从该节点到树根之间的路径长度与结点上权的乘机, 数的带权路径长度为树中所有叶子结点的带权路径长度之和。 假设有n个权值,(w1, w2, ... wn), 构造一棵有n个结点的二叉树, 每个叶子结点带权 wk, 每个叶子结点的路径长度为 lk, 我们通常记做, 其中带权路径长度 WPL最小的二叉树为赫夫曼树
2 如何构建赫夫曼树
- 先把有权值的叶子结点按照从小到大的顺序排成一个有序序列, 即: A5 E10 B15 D30 C40
- 取头两个最小权值的结点作为一个新结点N1的两个子节点, 注意取相对较小的是左孩子, 这里就是A为N1的左孩子, E为N1的右孩子, 新结点的权值为两个叶子权值的和 5 + 10 = 15
- 将N1替换A与E, 插入有序序列中, 保持从小到大排列, 即 N115, B15, D30, C40
- 重复步骤2, 将N1和B作为一个新结点N2 的两个子节点, 如图所示:
N2的权值为 15 +15 = 30
5.将N2 替换 N1 和B, 插入有序序列中, 保持从小到大排序, 即: N230, D30, C40
- 重复步骤2, 将N2 与D作为一个新结点, N3的两个子结点, N3的权值是 60
7.将N3 替换N2 和D, 插入有序序列中, 保持从小到大排列, 即 C40, N360
8 重复步骤2, 将C于N3作为一个新结点T的两个子节点, 用于T即是跟结点, 完成赫夫曼树的构造。
此时构造出来的树的权值 WPL = 401 +302 + 153 +104 + 5*4 = 205
通过刚才的步骤分析, 可以得出构造赫夫曼树的算法描述:
- 根据给定的N个权值{w1, w2, w3, ... wn} 构成n棵二叉树的集合 F={T1,T2,...Tn}, 其中每棵二叉树 Ti 中, 只有一个带权wi 根节点, 其左右子树均为空
- 在F中, 选取两个根节点的权值最小的树作为左右子树构成一个新的二叉树, 且置新的二叉树的根节点的权值为其左右子树的权值的和
- 在F中删除这两棵树, 同时将新得到的二叉树加入F中
4 重复2 和3步骤, 直到F中只含一棵树为止, 这棵树就是 赫夫曼树。
假设 6个字母的频率为 A27, B8, C15, D15, E30 , F5
如果我们中序遍历这个赫夫曼树的叶子结点, 则其结果应该是
DAFBCE
注意事项: 在生成哈夫曼树的时候, 每次都要取权重最小的结点, 所以每次从集合 F中取结点时, 都要先对结点进行排序, 然后再取最小的两个值。
第一次做的时候, 先对最原始的结点按照权重进行排序, 然后生成哈夫曼树, 生成错了, 耽误了好久.
代码如下:
代码 参考地址:
https://gitee.com/babyb/data_srtuct/tree/master/012Huffman
package main
import (
"fmt"
)
type Node struct{
value rune
weight int
lchild *Node
rchild *Node
}
//Nodes 用于Node, 按照weight 排序
type Nodes []Node
//Tree 就是整棵Huffman树的根节点
type Tree struct{
Root *Node
}
// 哈夫曼树
func main() {
a := Node{
value: 'A',
weight: 27,
}
b := Node{
value: 'B',
weight: 8,
}
c := Node{
value: 'C',
weight: 15,
}
d := Node{
value: 'D',
weight: 15,
}
e := Node{
value: 'E',
weight: 30,
}
f := Node{
value: 'F',
weight: 5,
}
nodes :=[]Node{a,b,c,d,e,f}
// //按照权重排序
// sorted := sortNodes(nodes);
// //展示排序后的切片
// showNodes(sorted);
//生成哈夫曼树
tree := makeHuffManTree(nodes);
//中序遍历二叉树, 展示叶子结点
root := tree.Root;
showLeafNode(root)
}
//返回排好序的 Nodes
func sortNodes(nodes Nodes) (sorted []Node){
for _, v := range nodes{
if len(sorted) == 0{
sorted = append(sorted, v);
}else{
i:=0;
for i