赫夫曼树
一、什么是赫夫曼树?
如果给出一个学生的成绩从百分制转化为五级制,若使用ifelse则代码过于冗余,我们可以使用赫夫曼树的形式完成它:
但是出于查找更高效的考虑,我们可以将树构建成这样:
这样效率又高了不少。
二、赫夫曼树的定义与原理
我们将上面这颗二叉树简化成带权二叉树:
从树中一个节点到另一个节点之间的分支构成两个节点之间的路径,路径上的分支数目称做路径长度。 如a中根节点到D的路径长度为4,b中到D的路径长度为2
树的路径长度就是从树根到每一节点的路径长度之和。 a的树路径长度就为1+1+2+2+3+3+4+4=20,b的树路径长度就为1+2+3+4+2+1+2+2=16
节点的带权路径长度就为从该节点到树根之间的路径与结点上权的乘积。树的带权路径长度就为树中所有叶子节点的带权路径长度之和。假设有n个权值{w1,w2,w3,wn},构造一颗有n个叶子节点的二叉树,每个叶子节点带权wk,每个叶子的路径长度为lk,则称其中带权路径长度WPL最小的二叉树为赫夫曼树。 也称最优二叉树。
那么a的WPL为 5 ∗ 1 + 15 ∗ 2 + 40 ∗ 3 + 30 ∗ 4 + 10 ∗ 5 = 315 5*1+15*2+40*3+30*4+10*5=315 5∗1+15∗2+40∗3+30∗4+10∗5=315
b的WPL为 5 ∗ 3 + 15 ∗ 3 + 40 ∗ 2 + 30 ∗ 2 + 10 ∗ 2 = 220 5*3+15*3+40*2+30*2+10*2=220 5∗3+15∗3+40∗2+30∗2+10∗2=220
三、如何构建赫夫曼树
构建赫夫曼树的步骤:
- 将n个权值作为n个根节点,从小到大排序为一个有序的树集合
- 取出权值最小的两个树构建一颗新树,作为新树N的两个孩子
- 将N插入有序集合中,重新保持有序
- 重复步骤2,直到只含一棵树为止
四、赫夫曼树的应用
最基本的压缩编码方式:赫夫曼编码
当年赫夫曼研究赫夫曼树的目的是为了优化电报的数据传输,例如我们的原文为BADCADFEED,并且知道六个字母的频率为A 27、B 8、C 15、D 15、E 30、F 5,合起来为100%,则我们可以构建一颗赫夫曼树:
并规定,左分支为0,右分支为1,那么我们可以得到这样的:
字母 | A | B | C | D | E | F |
---|---|---|---|---|---|---|
二进制字符 | 01 | 1001 | 101 | 00 | 11 | 0000 |
这样我们将原文就可以压缩为1001010010101001000111100,相比于其他的规定同样长度的01串短了很多。
如果要设计长短不等的编码,则需要任一字符的编码都不是另一字符的前缀,这种编码称做前缀编码。
给定字符集和各字符出现的次数或频率,以次数或频率作为叶子节点的权值来构建一个赫夫曼树,并规定左分支代表0,右分支代表1,则从根节点到叶子节点所经过的路径分治组成的01序列则为该节点对应字符的编码,称之为赫夫曼编码。
五、赫夫曼树的相关性质
赫夫曼树有他的一些性质:
- 是正则二叉树,即树中只有度为0或2的节点
- 有 n0 = n2 + 1,而 n = n0 + n2,故而 n0 = (n+1) /2,其中n0为叶节点,即编码
六、赫夫曼树的代码实现
这里给出赫夫曼书的Java实现:
import java.util.ArrayList;
import java.util.List;
/**
* 赫夫曼树
*
* auther:XingTL
* date:2020/9/30 19:03
*/
public class HuffmanTree {
//节点类
static class Node{
int val;//权值
Node left;
Node right;
public Node(int val){
this.val = val;
}
}
private Node root;
public HuffmanTree(int[] vals){
init(vals);
}
public HuffmanTree(){
root = null;
}
//构建一颗赫夫曼树
public void init(int[] vals){
if(root != null){
//只能初始化一次
return;
}
List<Node> list = new ArrayList<>();
for (int i = 0; i < vals.length; i++) {
list.add(new Node(vals[i]));
}
while(list.size() > 1){
Node n1 = getMin(list);
Node n2 = getMin(list);
Node node = new Node(n1.val+n2.val);
node.left = n1;
node.right = n2;
list.add(node);
}
root = list.get(0);
}
//获取最小值并将其从list中移除
private Node getMin(List<Node> list){
if(list.isEmpty()){
throw new RuntimeException();
}
int index = 0;
Node min = list.get(index);
for (int i = 1; i < list.size(); i++) {
Node t = list.get(i);
if(t.val < min.val){
min = t;
index = i;
}
}
list.remove(index);
return min;
}
//main 测试
public static void main(String[] args) {
int[] vals = {27,8,15,15,30,5};
HuffmanTree huffmanTree = new HuffmanTree(vals);
System.out.println();
}
}
参考文献:《大话数据结构》