哈夫曼编码原理:
案例代码实现:
package com.atguigu.huffmantree;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HuffmanCodes {
public static void main(String[] args) {
String content = "i like like like java do you like a java";
byte[] contentBytes = content.getBytes();
System.out.println(contentBytes.length);
List<Node> nodes = getNodes(contentBytes);
System.out.println("nodes=" + nodes);
//测试一把,创建的赫夫曼树
System.out.println("赫夫曼树");
Node huffmanTreeRoot = createHuffmanTree(nodes);
System.out.println("前序遍历");
huffmanTreeRoot.preOrder();
//测试一把是否生成了对应的赫夫曼编码
Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
System.out.println("~生成的赫夫曼编码表= " + huffmanCodes);
}
// 生成赫夫曼树对应的赫夫曼编码
// 思路:
// 1. 将赫夫曼编码表存放在 Map<Byte,String> 形式
// 生成的赫夫曼编码表{32=01, 97=100, 100=11000, 117=11001, 101=1110, 118=11011, 105=101,
// 121=11010, 106=0010, 107=1111, 108=000, 111=0011}
static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
// 2. 在生成赫夫曼编码表示,需要去拼接路径, 定义一个StringBuilder 存储某个叶子结点的路径
static StringBuilder stringBuilder = new StringBuilder();
// 重载getCodes
private static Map<Byte, String> getCodes(Node root){
if(root == null) {
return null;
}
// 处理root的左子树
getCodes(root.left, "0", stringBuilder);
// 处理root的右子数
getCodes(root.right, "1", stringBuilder);
return huffmanCodes;
}
/**
* 功能:将传入的node结点的所有叶子结点的赫夫曼编码得到,并放入到huffmanCodes集合
*
* @param node
* 传入结点
* @param code
* 路径: 左子结点是 0, 右子结点 1
* @param stringBuilder
* 用于拼接路径
*/
private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
// 将code加入到 stringBuilder2
stringBuilder2.append(code);
if(node != null) { // 如果node == null不处理
// 判断当前node 是叶子节点还是非叶子节点
if(node.data == null) {
// 递归处理
// 向左递归
getCodes(node.left, "0", stringBuilder2);
// 向右递归
getCodes(node.right, "1", stringBuilder2);
}else {
huffmanCodes.put(node.data, stringBuilder2.toString());
}
}
}
// 前序遍历
public static void preOrder(Node root) {
if (root != null) {
root.preOrder();
} else {
System.out.println("赫夫曼树为空");
}
}
/**
* @param bytes
* 接收字节数组
* @return 返回的就是 List 形式 [Node[date=97 ,weight = 5], Node[]date=32,weight =
* 9]......],
*/
// 统计每个byte 的出现次数
private static List<Node> getNodes(byte[] bytes) {
// 1 创建一个ArrayList
ArrayList<Node> nodes = new ArrayList<Node>();
// 2 遍历bytes , 统计每一个byte出现的次数-> map[key, value]
Map<Byte, Integer> counts = new HashMap<>();
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
// 3 把每一个键值对转成一个Node对象,并加入到nodes集合
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
// 创建赫夫曼树
public static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
// 先对节点进行排序
Collections.sort(nodes);
// 取出前两个节点
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
// 创建一颗新的二叉树,它的根节点只有权值,没有data
Node parentNode = new Node(null, leftNode.weight + rightNode.weight);
parentNode.left = leftNode;
parentNode.right = rightNode;
// 将已经处理的两颗二叉树从nodes删除
nodes.remove(leftNode);
nodes.remove(rightNode);
// 将新的二叉树放入nodes
nodes.add(parentNode);
}
// 返回赫夫曼树的根节点
return nodes.get(0);
}
}
class Node implements Comparable<Node> {
Byte data; // 存储数据所对应的字节
int weight; // 存储当前节点的权重
Node left;
Node right;
public Node(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
public int compareTo(Node o) {
// 从小到大排序
return this.weight - o.weight;
}
@Override
public String toString() {
return "Node [data=" + data + ", weight=" + weight + "]";
}
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
}
结果截图: