目录
一:赫夫曼编码的介绍
1 赫夫曼编码也翻译为哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式, 属于一种程序算法。
2 赫夫曼编码是赫哈夫曼树在电讯通信中的经典的应用之一。
3 赫夫曼编码广泛地用于数据文件压缩。 其压缩率通常在20%~90%之间。
4 赫夫曼码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,称之为最佳编码。
二:赫夫曼编码原理解析
2.1 通信领域中信息的处理方式1——定长编码
2.2 通信领域中信息的处理方式2——变长编码
2.3 通信领域中信息的处理方式3——赫夫曼编码
三:赫夫曼编码步骤
1 假设要传输的字符串为:i like like like java do you like a java
2 统计各个字符对应的个数:d:1 y:1 u:1 j:2 v:2 o:2 l:4 k:4 e:4 i:5 a:5 :9
3 按照上面字符出现的次数构建一颗赫夫曼树, 次数作为权值。
构建赫夫曼树的步骤如下:
3.1 从小到大进行排序,每个数据都是一个节点,每个节点可以看成是一颗最简单的二叉树。
3.2 取出根节点权值最小的两颗二叉树。
3.3 组成一棵新的二叉树, 该新的二叉树的根节点的权值是前面两颗二叉树根节点权值的和。
3.4 再将这棵新的二叉树,以根节点的权值大小再次排序,不断重复1-2-3-4 的步骤,直到数列中,所有的数据都被处理,就得到一颗赫夫曼树。
4 根据赫夫曼树,给各个字符,规定编码 (前缀编码),向左的路径为 0, 向右的路径为 1 ,编码如下:
o:1000 u:10010 d:100110 y:100111 i:101 a:110 k:1110 e:1111 j:0000 v:0001 l:001 :01
5 按照上面的赫夫曼编码,"i like like like java do you like a java" 字符串对应的编码如下:
1010100110111101111010011011110111101001101111011110100001100001110011001111000011001111000100100100110111101111011100100001100001110
注意:这里使用的是无损压缩。
6 通过赫夫曼编码处理后,长度变为133。
四:说明
1 原来长度是359,压缩了(359-133) / 359 = 62.9%。
2 此编码满足前缀编码, 即字符的编码都不能是其他字符编码的前缀,不会造成匹配的多义性。
3 赫夫曼编码是无损处理方案。
注意
赫夫曼树根据排序方法不同,也可能不太一样,这样对应的赫夫曼编码也不完全一样,但是 wpl 是一样的,都是最小的,最后生成的赫夫曼编码的长度是一样。
比如:如果让每次生成的新的二叉树总是排在权值相同的二叉树的最后一个,则生成的二叉树为下图。
五:赫夫曼编码代码展示
赫夫曼树结点的定义
package tree;
public class HFMCode implements Comparable<HFMCode>{
private Byte b;
private int weight;
private HFMCode left;
private HFMCode right;
public HFMCode(Byte b, int weight) {
this.b = b;
this.weight = weight;
}
public Byte getB() {
return b;
}
public void setB(Byte b) {
this.b = b;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public HFMCode getLeft() {
return left;
}
public void setLeft(HFMCode left) {
this.left = left;
}
public HFMCode getRight() {
return right;
}
public void setRight(HFMCode right) {
this.right = right;
}
@Override
public String toString() {
return "HFMCode [b=" + b + ", weight=" + weight + "]";
}
@Override
public int compareTo(HFMCode o) {
// TODO Auto-generated method stub
return this.weight - o.weight;
}
public void preSelect() {
System.out.println(this);
if(this.getLeft() != null) {
this.getLeft().preSelect();
}
if(this.getRight() != null) {
this.getRight().preSelect();
}
}
}
赫夫曼编码
package tree;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HFMCTree {
private static Map<Byte,String> map = new HashMap<>();;
public static void main(String[] args) {
String str = "Im RenBiaoYu what your name";
byte[] bytes = str.getBytes();
HFMCode root = creatTree(counts(bytes));
Map<Byte,String> m = creatCode(root,"",new StringBuilder());
System.out.print(Arrays.toString(zip(m, bytes)));
}
//统计个数
public static ArrayList<HFMCode> counts(byte[] arr){
Map<Byte,Integer> map = new HashMap<>();
for(byte b:arr) {
Integer i = map.get(b);
if(i == null) {
map.put(b, 1);
}else {
map.put(b, i + 1);
}
}
//压缩到List集合中方便后序赫夫曼树的建立
ArrayList<HFMCode> al = new ArrayList<HFMCode>();
for(Map.Entry<Byte, Integer> entry:map.entrySet()) {
al.add(new HFMCode(entry.getKey(),entry.getValue()));
}
return al;
}
//创建赫夫曼树
public static HFMCode creatTree(ArrayList<HFMCode> hfmc) {
while(hfmc.size() > 1) {
Collections.sort(hfmc);
HFMCode left = hfmc.get(0);
HFMCode right = hfmc.get(1);
HFMCode newCode = new HFMCode(null,left.getWeight() + right.getWeight());
newCode.setLeft(left);
newCode.setRight(right);
hfmc.remove(left);
hfmc.remove(right);
hfmc.add(newCode);
}
return hfmc.get(0);
}
//赫夫曼编码
public static Map<Byte,String> creatCode(HFMCode node, String code, StringBuilder sb){
StringBuilder stringCode = new StringBuilder(sb);
stringCode.append(code);
if(node != null) {
if(node.getB() == null) {
creatCode(node.getLeft(),"0",stringCode);
creatCode(node.getRight(),"1",stringCode);
}else{
map.put(node.getB(), stringCode.toString());
}
}
return map;
}
//压缩数据
public static byte[] zip(Map<Byte,String> m,byte[] bytes) {
StringBuilder sb = new StringBuilder();
for(byte b:bytes) {
sb.append(m.get(b));
}
int length = 0;
if(sb.length() % 8 == 0) {
length = sb.length() / 8;
}else {
length = sb.length() / 8 + 1;
}
//压缩数据的数组
int index = 0;
byte[] newbytes = new byte[length];
for(int i = 0; i < sb.length(); i+=8) {
String str = "";
if(i + 8 > sb.length()) {
str = sb.substring(i);
}else {
str = sb.substring(i, i + 8);
}
newbytes[index++] = (byte) Integer.parseInt(str, 2);
}
return newbytes;
}
}