转载地址 : http://blog.csdn.net/xuefeng0707/article/details/7844834
哈夫曼编码:
一种字符编码方式,常用于数据文件压缩。压缩率通常在20%~90%。
主要思想:
采取可变长编码方式,对文件中出现次数多的字符采取比较短的编码,对于出现次数少的字符采取比较长的编码,可以有效地减小总的编码长度。
例如,在英文中,e的出现频率最高,z的出现频率最低,所以可以用最短的编码来表示e,用最长的编码表示z。
例子:
一个文件包含100 000个字符,且仅含有a,b,c,d,e,f六个字符,那么可以用3位bit来编码这六个字符,称之为定长码。
那么采取定长码共需要的位数为 3* 100 000 = 600 000位。
这六个字符出现的频率如图所示,a最多,f最少。因此如果采取图中所示的变长码,所需要的位数为:
(45*1 + 13*3 + 12 * 3 + 16*3 + 9*4 + 5*4) * 1000 = 224 000。
可以看出压缩了25%,a的编码长度最短,为一位,f的编码长度最长,为4位。
前缀码:
当读取文件时,对于定长码文件,已经知道了每个字符的编码长度,所以只需按部就班的一个一个的读取字符即可;
对于变长码文件,各个字符的编码长度不一,因此需要小心设计各个字符的编码,一面混淆。
比如,如果a的编码为0,b的编码为01,c的编码为1,那么解码的时候如果遇到‘001’,则既可以解码成‘aac’,也可以解码成‘ab’。
因为b的编码中包含了a的编码,也就是a的编码是b的编码的前缀。
所以,变长码编码的设计,每个字符的编码都不能是其他字符编码的前缀,这种方式称之为前缀码。
可以用二叉树来表示每个字符的编码,以左为0,以右为1,这样每个叶子节点的路径均不同,也都不会称为其他叶子节点的前缀。
这样每个叶子节点的路径,就是每个字符的编码。
图中形成的编码与表格中的有些差异,但是各个字符编码的长度都相同,这是左子树和右子树的区别,但是对编码长度没有影响。
贪心算法代码实现:
/**
* Huffman code
* @author xuefeng
*
*/
public class Huffman {
/**
* ignore exception
* @param cs : characters
* @param freqs : frequency of each character
*/
public static void huffman(char[] cs, double[] freqs) {
int n = cs.length;
MinHeap<Code> heap = new MinHeap<Code>(cs.length);
Code[] codes = new Code[n];
for (int i = 0; i < n; i++) {
Code c = new Code(cs[i], freqs[i]);
heap.add(c); // 以最小堆来每次取出频率最小的两个
codes[i] = c; // 保存所有的叶子节点
}
Code c, c1, c2;
while (heap.size() > 1) {
c1 = heap.removeMin();
c2 = heap.removeMin();// 取出两个最小的
c = new Code('\0', c1.freq + c2.freq);
c.left = c1;
c.right = c2;
c1.parent = c;
c2.parent = c;
heap.add(c); // 组合成一个新的节点,并放入堆中,继续比较
System.out.println(c1.val + "+" + c2.val + " : " + c1.freq + "+" + c2.freq + " = " + c.freq);
}
c = heap.removeMin(); // 二叉树根节点
StringBuffer sb;
for (int i = 0; i < n; i++) {
c = codes[i]; // 从每个叶子节点,向上追溯,直到根节点,确定每个字符的编码
sb = new StringBuffer();
while (c != null) {
if (c.parent != null) {
if (c == c.parent.left) {
sb.insert(0, 0); // 如果是左边的,编码取1
} else {
sb.insert(0, 1); // 如果是右边的,编码取1
}
}
c = c.parent;
}
System.out.println(codes[i].val + " : " + sb.toString());
}
}
public static void main(String[] args) {
char[] cs = { 'a', 'b', 'c', 'd', 'e', 'f' };
double[] freqs = { 45, 13, 12, 16, 9, 5 };// %
huffman(cs, freqs);
// http://zh.wikipedia.org/wiki/%E5%AD%97%E6%AF%8D%E9%A2%91%E7%8E%87
char[] cs2 = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z' };
double[] freqs2 = { 8.167, 1.492, 2.782, 4.253, 12.702, 2.228, 2.015,
6.094, 6.966, 0.153, 0.772, 4.025, 2.406, 6.749, 7.507, 1.929,
0.095, 5.987, 6.327, 9.056, 2.758, 0.978, 2.360, 0.150, 1.974,
0.074 };
huffman(cs2, freqs2);
}
}
class Code implements Comparable<Code> {
public char val;
public double freq;
public Code left, right, parent;
public Code(char val, double freq) {
this.val = val;
this.freq = freq;
}
@Override
public int compareTo(Code c) {
double d = freq - c.freq;
return d > 0 ? 1 : (d == 0 ? 0 : -1);
}
}