哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。其压缩率通常在20%~90%之间。哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。

给出现频率高的字符较短的编码,出现频率较低的字符以较长的编码,可以大大缩短总码长。

贪心算法之哈夫曼编码

定长码:

3*(45+13+12+16+9+5) = 300 千位

变长码:

1*45+3*13+3*12+3*16+4*9+4*5 = 224 千位

1、前缀码

对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其它字符代码的前缀。这种编码称为前缀码

编码的前缀性质可以使译码方法非常简单。

表示最优前缀码的二叉树总是一棵完全二叉树,即树中任一结点都有2个儿子结点。

f(c)表示字符c出现的概率,dt(c)表示c的码长

平均码长定义为:贪心算法之哈夫曼编码

使平均码长达到最小的前缀码编码方案称为给定编码字符集C的最优前缀码

2、构造哈夫曼编码

哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼编码

哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。

算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树T。

以f为键值的优先队列Q用在贪心选择时有效地确定算法当前要合并的2棵具有最小频率的树。一旦2棵具有最小频率的树合并后,产生一棵新的树,其频率为合并的2棵树的频率之和,并将新树插入优先队列Q。经过n-1次的合并后,优先队列中只剩下一棵树,即所要求的树T。

算法huffmanTree用最小堆实现优先队列Q。初始化优先队列需要O(n)计算时间,由于最小堆的removeMin和put运算均需O(logn)时间,n-1次的合并总共需要O(nlogn)计算时间。因此,关于n个字符的哈夫曼算法的计算时间为O(nlogn) 。

3、哈夫曼算法的正确性

要证明哈夫曼算法的正确性,只要证明最优前缀码问题具有贪心选择性质和最优子结构性质。

(1)贪心选择性质

(2)最优子结构性质

具体代码实现:

import java.util.LinkedHashMap;
import java.util.ArrayList;
import java.util.Set;
import java.util.Iterator;
   5:  
class HuffmanNode {
char label;
int weight;
int parent;
int lChild;
int rChild;
//频率主要是用来衡量字符在给定编码字符串中出现的次数
  13:  
int lChild,
int rChild) {
this.label = label;
this.weight = weight;
this.lChild = lChild;
this.rChild = rChild;
  20:     }
  21: }
  22:  
class HuffmanTree {
//主要用hashmap来存放字符及其出现的频率
private Set<Character> charset;
//huffman节点集合
//huffman编码集合
  28:  
public HuffmanTree(LinkedHashMap<Character, Integer> map) {
  30:         charTable = map;
  31:         charset = map.keySet();
//首先创建huffman树
  33:         creatHuffmanCode();
  34:     }
  35:  
void initTree() {
new ArrayList<HuffmanNode>();
  38:         Iterator<Character> charIter = charset.iterator();
int i = 1;
  40:         huffmanTree.add(0,
char) 0, Integer.MAX_VALUE, 0, 0, 0));
while (charIter.hasNext()) {
  43:             Character ch = charIter.next();
new HuffmanNode(ch, charTable.get(ch), 0, 0, 0));
  45:             i++;
  46:         }
int j = charset.size() + 1; j < 2 * charset.size(); j++) {
char) 0, 0, 0, 0, 0));
  49:         }
  50:     }
  51:     
// 创建huffman树
void creatHuffmanTree() {
  54:         initTree();
int min_child1;
int min_child2;
int i = charset.size() + 1; i < 2 * charset.size(); i++) {
  58:             min_child1 = 0;
  59:             min_child2 = 0;
int j = 1; j < i; j++) {
if (huffmanTree.get(j).parent == 0) {
if (huffmanTree.get(j).weight < huffmanTree.get(min_child1).weight
  63:                             || huffmanTree.get(j).weight < huffmanTree
  64:                                     .get(min_child2).weight) {
if (huffmanTree.get(min_child1).weight < huffmanTree
  66:                                 .get(min_child2).weight) {
  67:                             min_child2 = j;
else {
  69:                             min_child1 = j;
  70:                         }
  71:                     }
  72:                 }
  73:             }
  74:             huffmanTree.get(min_child1).parent = i;
  75:             huffmanTree.get(min_child2).parent = i;
  76:  
if (min_child1 < min_child2) {
  78:                 huffmanTree.get(i).lChild = min_child1;
  79:                 huffmanTree.get(i).rChild = min_child2;
else {
  81:                 huffmanTree.get(i).rChild = min_child1;
  82:                 huffmanTree.get(i).lChild = min_child2;
  83:             }
  84:  
  85:             huffmanTree.get(i).weight = huffmanTree.get(i).weight
  86:                     + huffmanTree.get(i).weight;
  87:         }
  88:     }
  89:  
void creatHuffmanCode() {
new ArrayList<String>(charset.size() + 1);
  92:         huffmanCode.add(0, null);
char[charset.size() + 1];
int i = 1; i < charset.size() + 1; i++) {
int startIndex = charset.size();
int parent = huffmanTree.get(i).parent;
int ch = i;
while (parent != 0) {
if (huffmanTree.get(parent).lChild == ch) {
'0';
else {
'1';
 103:                 }
 104:                 startIndex--;
 105:                 ch = parent;
 106:                 parent = huffmanTree.get(parent).parent;
 107:             }
 108:             System.out.println(String.valueOf(tempChars, startIndex + 1,
 109:                     charset.size() - startIndex));
 110:             huffmanCode.add(i, String.valueOf(tempChars, startIndex + 1,
 111:                     charset.size() - startIndex));
 112:         }
// end method
 114:     
// huffman编码
public String enCodeString(String inString) {
new StringBuffer();
int i = 0; i < inString.length(); i++) {
int ch = inString.charAt(i);
int j = 1;
for (; huffmanTree.get(j).label != ch && j < charset.size() + 1; j++) {
 122:             }
if (j <= charset.size()) {
 124:                 temp.append(huffmanCode.get(j));
else {
 126:                 temp.append(ch);
 127:             }
 128:         }
return temp.toString();
 130:     }
 131:  
// huffman解码
public String deCodeString(String inString) {
new StringBuffer();
int root = charset.size() * 2 - 1;
int i = 0; i < inString.length(); i++) {
char ch = inString.charAt(i);
'0') {
 139:                 root = huffmanTree.get(root).lChild;
'1') {
 141:                 root = huffmanTree.get(root).rChild;
else {
 143:                 temp.append(ch);
 144:             }
if (root <= charset.size()) {
 146:                 temp.append(huffmanTree.get(root).label);
 147:                 root = charset.size() * 2 - 1;
 148:             }
 149:         }
return temp.toString();
 151:     }
 152:  
 153: }
 154:  
class HuffmanTreeTest {
void main(String[] args) {
new LinkedHashMap<Character, Integer>();
'a', 4);
'b', 5);
'c', 8);
'd', 10);
 162:  
new HuffmanTree(hasmap);
);
 165:         System.out.println(temp);
 166:         System.out.println(huffman.deCodeString(temp));
 167:  
 168:     }
 169:  
 170: }

相关文章: