哈夫曼树(Huffman Tree)是一种用于数据压缩的树形结构,广泛应用于无损数据压缩算法中。由大卫·哈夫曼(David A. Huffman)在1952年提出。它主要用于构造哈夫曼编码,一种基于字符出现频率的变长编码方案。
压缩的目的是使用更少的空间来存储比较大的数据内容。这样首先就要对原数据中重复内容进行统计,重复最多的内容使用最短编码来表示。最短编码对应到树上可以使用节点路径来表示:从根节点到任意一个叶子节点的路径。
这样将重复多的项放于靠近根节点位置,重复最少的尽量存放于最底层叶子节点即可。每个节点位置确定可以根据重复次数作为权重来进行确定。
树的构建:
1、首先统计每个字符在待压缩数据中出现的频率作为节点权重值。
2、将每个字符及其频率(权重)作为叶子节点,构建一个优先队列。
3、从优先队列中取出两个最小权重的节点。创建一个新的内部节点,其权重为这两个节点权重的和,将这两个节点作为新节点的子节点。将新节点插入优先队列。重复上述步骤直到队列中只剩下一个节点,该节点就是哈夫曼树的根节点。
编码生成:
从根节点开始,进行遍历。通常左子节点标记为0,右子节点标记为1。这样可以为每个字符生成一个唯一的二进制编码
代码演示:
首先定义一个Node节点
class Node<T> {
T data;
int weight;
Node left;
Node right;
Node parent;
public Node(T data,int weight){
this.data = data;
this.weight = weight;
}
}
Node节点除了必要的引用关系,外还有一个额外必须的属性weight(权重)。
树的构建
/**
* 先按权重进行排序
* 然后拿出两个最小的组成一个树,父节点为两个的权重和构造新节点,然后将这个节点再放入集合中
* 再拿出两个最小的重复前面的动作,直到集合只有一个元素,则该元素为根元素
* @param nodeList
*/
void buildTree(List<Node> nodeList){
if(nodeList.size() ==1) {
root = nodeList.get(0);
return;
}
while (nodeList.size() >1){
nodeList = sort(nodeList);
Node left = nodeList.remove(0);
Node right = nodeList.remove(0);
Node parent = new Node(null,left.weight + right.weight);
parent.left = left;
parent.right = right;
left.parent = parent;
right.parent = parent;
nodeList.add(parent);
}
root = nodeList.get(0);
}
List<Node> sort(List<Node> nodeList){
return nodeList.stream().sorted(Comparator.comparingInt(n -> n.weight)).collect(Collectors.toList());
}
字符编码获取:
/**
* 获取字符编码
* 到左节点路径0,右节点路径为1
* @param node
* @return
*/
String getCode(Node node){
String code = "";
Node tmp = node;
while (tmp.parent != null){
if(tmp.parent.left == tmp){
code = "0" + code;
}else{
code = "1" + code;
}
tmp = tmp.parent;
}
return code;
}