哈夫曼编码(Huffman Coding)是一种编码方式,也称为“赫夫曼编码”,是David A. Huffman1952年发明的一种构建极小多余编码的方法。
在计算机数据处理中,霍夫曼编码使用变长编码表对源符号进行编码,出现频率较高的源符号采用较短的编码,出现频率较低的符号采用较长的编码,使编码以后的字符串字符串的平均长度 、指望值下降,以达到无损压缩数据的目的。
举个例子,如今咱们有一字符串:html
this is an example of a huffman treenode
这串字符串有36个字符,若是按普通方式存储这串字符串,每一个字符占据1个字节,则共须要36 * 1 * 8 = 288bit。
通过分析咱们发现,这串字符串中各字母出现的频率不一样,若是咱们可以按以下编码:git
字母 | 频率 | 编码 | --- | 字母 | 频率 | 编码 |
---|---|---|---|---|---|---|
space | 7 | 111 | s | 2 | 1011 | |
a | 4 | 010 | t | 2 | 0110 | |
e | 4 | 000 | l | 1 | 11001 | |
f | 3 | 1101 | o | 1 | 00110 | |
h | 2 | 1010 | p | 1 | 10011 | |
i | 2 | 1000 | r | 1 | 11000 | |
m | 2 | 0111 | u | 1 | 00111 | |
n | 2 | 0010 | x | 1 | 10010 |
编码这串字符串,只须要:
(7+4+4)x3 + (3+2+2+2+2+2+2)x4 + (1+1+1+1+1+1)x 5 = 45+60+30 = 135bit
编码这串字符串只须要135bit!单单这串字符串,就压缩了288-135 = 153bit。github
那么,咱们如何获取每一个字符串的编码呢?这就须要哈夫曼树了。
源字符编码的长短取决于其出现的频率,咱们把源字符出现的频率定义为该字符的权值。数组
哈夫曼又称最优二叉树。是一种带权路径长度最短的二叉树。它的定义以下。数据结构
假设有n个权值{w1,w2,w3,w4...,wn},构造一棵有n个节点的二叉树,若树的带权路径最小,则这颗树称做哈夫曼树。这里面涉及到几个概念,咱们由一棵哈夫曼树来解释
函数
路径与路径长度:从树中一个节点到另外一个节点之间的分支构成了两个节点之间的路径,路径上的分支数目称做路径长度。若规定根节点位于第一层,则根节点到第H层的节点的路径长度为H-1.如树b:100到60 的路径长度为1;100到30的路径长度为2;100到20的路径长度为3。post
树的带权路径长度:树的带权路径长度为全部叶子节点的带权路径长度之和,称为WPL。树a的WPL = 2*(10+20+30+40) = 200 ;树b的WPL = 1x40+2x30+3x10+3x20 = 190.而哈夫曼树就是树的带权路径最小的二叉树。性能
/*哈夫曼树的节点定义*/ template <typename T> struct HuffmanNode { HuffmanNode(T k,HuffmanNode<T>*l=nullptr,HuffmanNode<T>* r=nullptr) :key(k),lchild(l), rchild(r){} ~HuffmanNode(){}; T key; //节点的权值 HuffmanNode<T>* lchild; //节点左孩 HuffmanNode<T>* rchild; //节点右孩 };
template <typename T> class Huffman { public: void preOrder(); //前序遍历哈夫曼树 void inOrder(); //中序遍历哈夫曼树 void postOrder(); //后序遍历哈夫曼树 void creat(T a[], int size); //建立哈夫曼树 void destory(); //销毁哈夫曼树 void print(); //打印哈夫曼树 Huffman(); ~Huffman(){}; private: void preOrder(HuffmanNode<T>* pnode); void inOrder(HuffmanNode<T>* pnode); void postOrder(HuffmanNode<T>*pnode); void print(HuffmanNode<T>*pnode); void destroy(HuffmanNode<T>*pnode); private: HuffmanNode<T>* root; //哈夫曼树根节点 deque<HuffmanNode<T>*> forest;//森林 };
假设有n个权值,则构造出的哈夫曼树有n个叶子节点.n个权值记为{w1,w2,w3...wn},哈夫曼树的构造过程为:测试
图一的树b为一棵哈夫曼树,它的叶子节点为{10,20,30,40},以这4个权值构建树b的过程为:
这个过程很编码实现为:
/*建立哈夫曼树*/ template<typename T> void Huffman<T>::creat(T a[],int size) { for (int i = 0; i < size; i++) //每一个节点都做为一个森林 { //为初始序列的元素构建节点。每一个节点做为一棵树加入森林中。 HuffmanNode<T>* ptr = new HuffmanNode<T>(a[i],nullptr,nullptr); forest.push_back(ptr); } for (int i = 0; i < size - 1; i++) { //排序,以选出根节点权值最小两棵树 sort(forest.begin(), forest.end(), [](HuffmanNode<T>* a, HuffmanNode<T>*b){return a->key< b->key; }); HuffmanNode<T>*node = new HuffmanNode<T>(forest[0]->key + forest[1]->key, forest[0], forest[1]); //构建新节点 forest.push_back(node); //新节点加入森林中 forest.pop_front(); //删除两棵权值最小的树 forest.pop_front(); } root = forest.front(); forest.clear(); };
其余操做在前几篇博文中都有介绍过,这里就再也不啰嗦,能够在文章底部连接取得完整的工程源码。
这里贴出测试时须要的代码:
/*打印哈夫曼树*/ template<typename T> void Huffman<T>::print(HuffmanNode<T>* pnode) { if (pnode != nullptr) { cout << "当前结点:" << pnode->key<<"."; if (pnode->lchild != nullptr) cout << "它的左孩子节点为:" << pnode->lchild->key << "."; else cout << "它没有左孩子."; if (pnode->rchild != nullptr) cout << "它的右孩子节点为:" << pnode->rchild->key << "."; else cout << "它没有右孩子."; cout << endl; print(pnode->lchild); print(pnode->rchild); } };
咱们构建上图中的哈夫曼树,它的四个权值分别为{10,20,30,40}:
测试代码:
int _tmain(int argc, _TCHAR* argv[]) { Huffman<int> huff; int a[] = { 10,20,30,40 }; huff.creat(a, 4); //构建一棵哈夫曼树 huff.print(); //打印节点间关系 getchar(); return 0; }
测试结果:
当前结点:100.它的左孩子节点为:40.它的右孩子节点为:60. 当前结点:40.它没有左孩子.它没有右孩子. 当前结点:60.它的左孩子节点为:30.它的右孩子节点为:30. 当前结点:30.它没有左孩子.它没有右孩子. 当前结点:30.它的左孩子节点为:10.它的右孩子节点为:20. 当前结点:10.它没有左孩子.它没有右孩子. 当前结点:20.它没有左孩子.它没有右孩子.
根据节点关系能够画出以下二叉树,正是上面咱们构建的哈夫曼树。
为{10,20,30,40}这四个权值构建了哈夫曼编码后,咱们能够由以下规则得到它们的哈夫曼编码:
(字母)权值 | 编码 |
---|---|
10 | 100 |
20 | 101 |
30 | 11 |
40 | 0 |
因而可知,出现频率越高的字母(也即权值越大),其编码越短。这便使编码以后的字符串的平均长度、指望值下降,从而达到无损压缩数据的目的。
哈夫曼树完整代码:https://github.com/huanzheWu/Data-Structure/blob/master/Huffman/Huffman/Huffman.h
更多数据结构C++实现代码:https://github.com/huanzheWu/Data-Structure
原创文章,转载请注明出处:http://www.cnblogs.com/QG-whz/p/5175485.html