用java代码实现哈夫曼树的构建,以及哈夫曼编码的数据压缩,同时也解决了数据恢复时出现与原字符串不同的问题。
前言
哈夫曼树是最优的二叉树,将带权的节点构建成一个带权路径WPL最小的二叉树。利用哈夫曼树的结构,对叶子节点进行编码,会得到一个哈夫曼编码集合,且这个集合里面任意两个元素的编码前缀都不一样,当数据匹配时能准确无误的找到对应的元素,因此哈夫曼编码适用于重复量多的数据进行压缩。
一、哈夫曼树的介绍
哈夫曼树:最优二叉树,也是带权路径长度最短的树。
以下是对一些带权二叉树的路径WPL计算:
可以看出,带权是指叶子节点是有值,当WPL的值小时,值大的叶子节点离根节点近,值小的叶子节点离根节点远。因此哈夫曼树是根据值的大小决定叶子节点的位置。
二、构建哈夫曼树
图解
给定数列{2,5,9,1,13,21},构建一颗哈夫曼树
- 寻找数列中最小的两个数作为叶子结点
- 将这两个元素从数列中移除,并将这两个节点的和放入数列中,此时数列为{5,9,13,21,3}
- 重复①②步骤,直到数列只剩下一个元素为止。
代码实现
(1)节点Node的定义
/**
* 节点
*/
public class Node implements Comparable<Node>{
private int value;
// 左子树
private Node left;
// 右子树
private Node right;
public Node(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
/**
* 重写Comparable接口的比较方法,将Node节点按照value的值从小到大排序
* @param o
* @return
*/
@Override
public int compareTo(Node o) {
return this.value - o.value;
}
}
(2)哈夫曼树的实现
/**
* 数列转换成哈夫曼树
*/
public Node arrTransToHuffmanTree(int[] arr){
if (arr == null || arr.length < 2) {
return null;
}
List<Node> nodeList = new ArrayList<>();
// 1. 将元素都转换成节点类型
for (int i = 0; i < arr.length; i++) {
nodeList.add(new Node(arr[i]));
}
while (nodeList != null && nodeList.size() > 1) {
// 2. 对数据进行排序
Collections.sort(nodeList);
// 3. 取出最小的两个元素
Node leftNode = nodeList.get(0);
Node rightNode = nodeList.get(1);
// 4. 两个元素之和作为这两个元素的父节点
Node parentNode = new Node(leftNode.getValue()+rightNode.getValue());
// 5. 这两个元素分别作为父节点的左右子节点
parentNode.setLeft(leftNode);
parentNode.setRight(rightNode);
// 6. 从数列中去除这两个节点
nodeList.remove(lef