赫夫曼编码
赫夫曼编码是一种变长编码方式,在通信中是一种经典的应用。广泛用于文件压缩,压缩率通常在20%~90%之间,主要通过使用的频率在最大化节约字符的存储空间。
实现步骤
- 统计各个字符的使用次数,用一个Node节点保存起来,具体data属性为字符对应的byte,weight权重代表他出现的次数
- 根据上述统计构建赫夫曼树
- 根据构建的赫夫曼树约定编码向左路径为0,右路径为1
- 单独创建一个全局的赫夫曼码表Map<byte,String> key为字符对应的byte String是该字符在这个赫夫曼树上的路径 由0和1拼接的字符串
- 递归赫夫曼树将所有叶子节点 记录节点的路径 添加赫夫曼码表中
- 如果要实现数据压缩,根据原文去赫夫曼码表中找对应的赫夫曼编码,拼接成一个新的编码,新生成的赫夫曼编码8位一个字节封装到一个byte数组中即可完成压缩
代码实现
public class HuffmanCodeDemo {
public static void main(String[] args) {
String content = "i like like like java do you like a java";
System.out.println(Arrays.toString(zip(content)));
}
/**
* 赫夫曼码表
*/
public static Map<Byte, String> huffmanCodeMap = new HashMap<>();
/**
* 得到赫夫曼编码并压缩的方法整合
*
* @param content
* @return
*/
public static byte[] zip(String content) {
HuffmanNode huffmanTree = createHuffmanNode(content);
getHuffmanCode(huffmanTree, "");
return getHuffmanZipCode(content.getBytes(), huffmanCodeMap);
}
/**
* @param content 原始文本的byte
* @param huffmanCodeMap
* @return
*/
public static byte[] getHuffmanZipCode(byte[] content, Map<Byte, String> huffmanCodeMap) {
// 1. 将原始字符串的字节数从对应的赫夫曼码表中找 然后拼接成一个新的字符串
StringBuilder builder = new StringBuilder();
for (byte b : content) {
builder.append(huffmanCodeMap.get(b));
}
// 2.每8位封装成一个byte数字 先获取数组长度
int len;
if (builder.length() % 8 == 0) {
len = builder.length() / 8;
} else {
len = builder.length() / 8 + 1;
}
byte[] zipContent = new byte[len];
int index = 0;
for (int i = 0; i < builder.length(); i += 8) {
String subContent;
if (i + 8 > builder.length()) {
subContent = builder.substring(i);
} else {
subContent = builder.substring(i, i + 8);
}
zipContent[index++] = (byte) Integer.parseInt(subContent, 2);
}
return zipContent;
}
/**
* 根据赫夫曼树生成赫夫曼编码 规定向左为0 向右为1
*
* @param node 赫夫曼树
* @param code 原始赫夫曼编码
* @return
*/
public static void getHuffmanCode(HuffmanNode node, String code) {
if (node != null) {
// 非数据节点
if (node.data == null) {
// 向左递归
// 向右递归
getHuffmanCode(node.left, code + "0");
getHuffmanCode(node.right, code + "1");
} else {
huffmanCodeMap.put((byte) node.data, code);
}
}
}
/**
* 将传入的字符串 拆分成byte 统计 每个byte 出现的次数然后生成一颗赫夫曼树
*
* @param content
* @return
*/
public static HuffmanNode createHuffmanNode(String content) {
if (null == content || content.length() == 0) {
return null;
}
// 1. 字符出现次数统计
Map<Byte, Integer> wordCount = new HashMap<>();
byte[] bytes = content.getBytes();
for (byte b : bytes) {
if (wordCount.get(b) == null) {
wordCount.put(b, 1);
} else {
wordCount.put(b, wordCount.get(b) + 1);
}
}
// 2. 将统计出来的字符串转成一个Node集合中用于生成 赫夫曼树
List<HuffmanNode> huffmanNodes = new ArrayList<>();
for (Byte key : wordCount.keySet()) {
huffmanNodes.add(new HuffmanNode(key, wordCount.get(key)));
}
// 3. 将Node集合构建成一颗赫夫曼树
while (huffmanNodes.size() > 1) {
Collections.sort(huffmanNodes);
HuffmanNode leftNode = huffmanNodes.get(0);
HuffmanNode rightNode = huffmanNodes.get(1);
HuffmanNode parent = new HuffmanNode(null, leftNode.weight + rightNode.weight);
parent.left = leftNode;
parent.right = rightNode;
huffmanNodes.remove(leftNode);
huffmanNodes.remove(rightNode);
huffmanNodes.add(parent);
}
return huffmanNodes.get(0);
}
}