首页 > 代码库 > 霍夫曼编码

霍夫曼编码

一. 霍夫曼编码和霍夫曼树

霍夫曼编码: 霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明霍夫曼树的WPL是最小的。

对霍夫曼编码问题的广义定义是:

Input: 一组符号(Symbol)和其对应的权重值(weight),其权重通常表示成概率或频率.

Output: 一组二元的前置码,其二元码的长度为最短。

二. 编码过程

1. 由节点森林创建霍夫曼树:

    1) 把n个终端节点加入优先队列,则n个节点都有一个优先权Pi,1 ≤ i ≤ n   

    2) 如果队列内的节点数>1,则:

a.从队列中移除两个最大的Pi节点,即连续做两次remove(max(Pi), Priority_Queue)
b.产生一个新节点,此节点为a所移除节点的父节点,而此节点的权重值为两节点之权重和
(在具体实现的时候应该确定左右孩子的大小关系,使生成的霍夫曼树是唯一的, 以方便解码)
c.把b产生的节点加入优先队列中

    3) 最后在优先队列里的点为树的根节点(root)

2. 由霍夫曼树生成霍夫曼编码:

    1) 将霍夫曼树的左链接标记为0, 右链接标记为1

    2) 从根结点到叶节点. 记录走过的路径标记, 即为叶节点所代表字符的霍夫曼编码.

技术分享     技术分享

三. 完整的实现代码

  1: #include <iostream>
  2: #include <hash_map>
  3: #include <queue>
  4: #include <set>
  5: #include <list>
  6: using namespace std;
  7: 
  8: struct Node
  9: {
 10: 	char symbol;
 11: 	int weight;
 12: 	Node* left;
 13: 	Node* right;
 14: 	Node(int w = 0, char s = ‘\0‘) :symbol(s), weight(w), left(0), right(0){}
 15: };
 16: 
 17: struct Node_comparator
 18: {
 19: 	bool operator()(const Node* a, const Node* b)
 20: 	{
 21: 		return !!(a->weight > b->weight);
 22: 	}
 23: };
 24: 
 25: typedef priority_queue<Node*, vector<Node*>, Node_comparator> Forest;
 26: Node* huffman(Forest& f);
 27: void encode(Forest& f);
 28: void encode(Node* n, list<char> li=list<char>());
 29: 
 30: Node* huffman(Forest& f)
 31: {
 32: 	while (f.size() > 1)
 33: 	{
 34: 		Node* const n1 = f.top(); f.pop();
 35: 		cout << n1->weight << endl;
 36: 		Node* const n2 = f.top(); f.pop();
 37: 		cout << n2->weight << endl;
 38: 		Node* nf = new Node(n1->weight + n2->weight);
 39: 		nf->left = (n1->weight < n2->weight) ? n2 : n1;
 40: 		nf->right = (n1->weight < n2->weight) ? n1 : n2;
 41: 		f.push(nf);
 42: 	}
 43: 	return f.top();
 44: }
 45: 
 46: void encode(Forest& f)
 47: {
 48: 	encode(huffman(f));
 49: }
 50: 
 51: void encode(Node* n, list<char> li)
 52: {
 53: 	if (n == 0) return;
 54: 	if (n->symbol != ‘\0‘)
 55: 	{
 56: 		for (auto c : li)
 57: 		{
 58: 			cout << c;
 59: 		}
 60: 		cout << ": " << n->symbol << endl;
 61: 		return;
 62: 	}
 63: 	list<char> l = list<char>(li);
 64: 	l.push_back(‘0‘);
 65: 	li.push_back(‘1‘);
 66: 	encode(n->left, l);
 67: 	encode(n->right, li);
 68: }

霍夫曼编码