前面一篇博客已经介绍了什么是haffman树以及如何创建一棵haffman树。 这节我们将介绍 关于haffman树最重要的一个应用 haffman编码 它是一种压缩效率很高的算法。
比如我们有这样一个字符串 ABAC BDCE DFEG FACA CCDE FG
假如把这段字符串存储起来,按照UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。每个字符是8个0和1,22 * 8 一共需要这么多个0 和1 表示。那如果我们用Haffman编码是多少个0 和1才能表示呢?
第一步、先统计字符的权重
A | B | C | D | E | F | G |
---|---|---|---|---|---|---|
4 | 2 | 5 | 3 | 3 | 3 | 2 |
按照权重我们排序结果是
B | G | D | E | F | A | C |
---|---|---|---|---|---|---|
2 | 2 | 3 | 3 | 3 | 4 | 5 |
按照上一节中我们的建树规则我们可以建一个这样的树
如上面的步骤 我们就建好了一个 haffman 树。 并且所有的左节点 标0 右节点标1,我们会得到这样的一个表
A | B | C | D | E | F | G |
---|---|---|---|---|---|---|
4 | 2 | 5 | 3 | 3 | 3 | 2 |
00 | 1110 | 01 | 100 | 101 | 110 | 1111 |
得到这个码表以后,我们可以根据这个码表进行重新的编码 ,我们用00 代表 A ,用1110 代表B
那么 ABAC BDCE DFEG FACA CCDE FG
得到的编码就是 00111000… -> ABA…
我编写一个测试代码验证一下
/**
* 根据表格的key和value 进行解码
* @param strEncoder
* @param table
* @return
*/
private String haffmanDecode(String strEncoder, TreeMap<String, String> table) {
int i = 1;
StringBuilder stringBuilder = new StringBuilder();
while (strEncoder.length() > 0){
String substring = strEncoder.substring(0, i);
Set<Map.Entry<String, String>> entries = table.entrySet();
for (Map.Entry<String, String> entry : entries) {
if (entry.getValue().equals(substring)){
stringBuilder.append(entry.getKey());
strEncoder = strEncoder.substring(i);
i = 1;
continue;
}
}
i++;
}
return stringBuilder.toString();
}
/**
* 根据表格 key和value 进行编码
* @param source
* @param table
* @return
*/
private String haffmanEncode(String source, TreeMap<String, String> table) {
int i =0 ;
StringBuilder stringBuilder = new StringBuilder();
while (i < source.length()){
char c = source.charAt(i);
String key = String.valueOf(c);
String s = table.get(key);
stringBuilder.append(s);
i++;
}
return stringBuilder.toString();
}
@Test
public void testHaffmanCodec(){
String source = "ABACBDCEDFEGFACACCDEFG";
//码表
TreeMap<String,String> table = new TreeMap<>();
table.put("A","00");
table.put("B","1110");
table.put("C","01");
table.put("D","100");
table.put("E","101");
table.put("F","110");
table.put("G","1111");
//编码后字符串是
String strEncode = haffmanEncode(source,table);
System.out.println(strEncode);
String strDecode = haffmanDecode(strEncode,table);
System.out.println(strDecode);
System.out.println("数据原本的大小是 "+(source.length() * 8));
System.out.println("编码后的大小是 "+(strEncode.length()));
}
输出结果:
0011100001111010001101100110101111111000010001011001011101111
ABACBDCEDFEGFACACCDEFG
数据原本的大小是 176
编码后的大小是 61
这样原本需要176个字符表示的数据, 现在我们只需要 61个0和1表示。 压缩的体积达到了50%以上