赫夫曼编码
基本介绍
- 赫夫曼编码也翻译为哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,属于一种程序算法。
- 赫夫曼编码是赫夫曼树在电讯通信中的经典应用之一,
- 赫夫曼编码广泛地应用于数据文件压缩,其压缩率通常在 20%~90% 之间。
- 赫夫曼编码是可变字长编码(VLC)的一种,Huffman 于 1952 年提出的一种编码。
注意:这个赫夫曼树根据排序方法不同,也可能不太一样,这样对应的赫夫曼编码也不完全一样,但是 wpl 是一样的,都是最小的。
数据压缩
思路分析
功能:根据赫夫曼编码压缩数据的原理,需要创建"i like like like java do you like a java"对应的赫夫曼树
思路:
- Node {data(存放数据),weight(权值),left,right}
- 得到"i like like like java do you like a java"对应的 byte[] 数组
- 编写一个方法将准备构建赫夫曼树的 Node 节点放到 List,形式:[Node[data = 97,weight = 5],…],体现 d:1 y:1 u:1 j:2 v:2 o:2 l:4 k:4 e:4 i:5 a:5 [空格]:9
- 可以通过 List 创建对应的赫夫曼树
- 将字符和权值加入到 List 集合
- 将得到的赫夫曼树的所有叶子结点的赫夫曼编码得到,并放入到 huffmanCodes 集合,得到赫夫曼编码表(规定:左子节点的路径为 0,右子节点的路径为 1)
- 将字符串对应的数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的数组
代码实现
public class HuffmanCode {
public static void main(String[] args) {
String content = "i like like like java do you like a java";
byte[] contentBytes = content.getBytes();
byte[] huffmanCodeBytes = huffmanZip(contentBytes);
System.out.println(Arrays.toString(huffmanCodeBytes));
}
/**
* 封装方法,便于调用
*
* @param bytes 原始的字符串对应的字节数组
* @return 是经过赫夫曼编码压缩过后的字节数组
*/
private static byte[] huffmanZip(byte[] bytes) {
// 将字符和权值加入到 List 集合
List<Node> nodes = getNodes(bytes);
// 通过 List 创建对应的赫夫曼树
Node huffmanTreeRoot = createHuffmanTree(nodes);
// 生成赫夫曼编码
Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
// 根据生成的赫夫曼编码,将字符数组进行压缩,得到压缩后的字符数组
return zip(bytes, huffmanCodes);
}
/**
* 编写方法,将字符串对应的 byte[] 数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的 byte[]
*
* @param bytes 原始字符串对应的 byte[]
* @param huffmanCodes 生成的赫夫曼编码 map
* @return 返回赫夫曼编码处理后的 byte[]
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
// 1. 利用 StringBuilder 将 bytes 转成赫夫曼编码
StringBuilder stringBuilder = new StringBuilder();
// 遍历 bytes
for (byte b : bytes) {
stringBuilder.append(huffmanCodes.get(b));
}
// 将拼接的字符串转为 byte[]
// 统计返回 byte[] huffmanCodeBytes 长度
int len;
if (stringBuilder.length() % 8 == 0) {
len = stringBuilder.length() / 8;
} else {
len = stringBuilder.length() / 8 + 1;
}
// 创建存储压缩后的 byte 数组
byte[] huffmanCodeBytes = new byte[len];
int index = 0; // 记录是第几个 byte
for (int i = 0; i < stringBuilder.length(); i += 8) {
// 因为是每 8 位对应一个 byte,所以步长为 8
String stByte;
if (i + 8 > stringBuilder.length()) {
// 不够 8 位
stByte = stringBuilder.substring(i);
} else {
stByte = stringBuilder.