每日一题(9)——Huffman编码(贪心)

来源:互联网 发布:电信网络是无线电短波 编辑:程序博客网 时间:2024/06/07 20:18

哈夫曼编码是典型的贪心问题,不断地通过局部最优,最终计算全局最优结果。

 

1951年,霍夫曼和他在MIT信息论的同学需要选择是完成学期报告还是期末考试。导师Robert M. Fano给他们的学期报告的题目是,查找最有效的二进制编码。由于无法证明哪个已有编码是最有效的,霍夫曼放弃对已有编码的研究,转向新的探索,最终发现了基于有序频率二叉树编码的想法,并很快证明了这个方法是最有效的。

由于这个算法,学生终于青出于蓝,超过了他那曾经和信息论创立者克劳德·香农共同研究过类似编码的导师。霍夫曼使用自底向上的方法构建二叉树,避免了次优算法Shannon-Fano编码的最大弊端──自顶向下构建树。

为每个符号建立一个叶子节点,并加上其相应的发生频率

  1. 当有一个以上的节点存在时,进行下列循环:
    1. 把这些节点作为带权值的二叉树的根节点,左右子树为空
    2. 选择两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且至新的二叉树的根结点的权值为其左右子树上根结点的权值之和。
    3. 把权值最小的两个根节点移除
    4. 将新的二叉树加入队列中.
  2. 最后剩下的节点暨为根节点,此时二叉树已经完成。

示例


Huffman Algorithm


符号ABCDE计数157665概率0.384615380.179487180.153846150.153846150.12820513

在这种情况下,D,E的最低频率和分配分别为0和1,分组结合概率的0.28205128。现在最低的一双是B和C,所以他们就分配0和1组合结合概率的0.33333333在一起。这使得BC和DE所以0和1的前面加上他们的代码和它们结合的概率最低。然后离开只是一个和BCDE,其中有前缀分别为0和1,然后结合。这使我们与一个单一的节点,我们的算法是完整的。


 

 

 

可得A代码的代码长度是1比特,其余字符是3比特。

字符ABCDE代码0100101110111

    Entropy:


 

哈夫曼算法实现

//参考sophia_qing的priotity_queue做法#include <iostream>#include <queue>#include <vector>#include <map>#include <iterator>#include <string>using namespace std;#define Nchar 8 //using 8 bit to describe all symbols#define Nsymbols 1<<Nchar //can describe 256 symbols totally (include a-z, A-Z)typedef vector<bool> Huff_code;map<char, Huff_code> Huff_dic;class Hnode {public:char data;float fre;Hnode *left, *right;Hnode(){data='\0'; fre=0; left=NULL; right=NULL;}Hnode(char c, float f, Hnode* l, Hnode* r){data=c; fre=f; left=l; right=r;}~Hnode(){delete left; delete right;}bool isLeaf(){ return !left&&!right;}};//只能为类类型或枚举类型的操作数定义重载操作符;//在把操作符声明为类的成员时,至少有一个类或枚举类型的参数按照值或者引用的方式传递;//因此不能在class Hnode内直接重载;(prepare for pointer sorting);class compare_node{public:bool operator () (Hnode* n1, Hnode* n2 ){return n1->fre > n2->fre;}};Hnode* bulidHtree(int *frequency){priority_queue<Hnode*, vector<Hnode*>, compare_node> Qtree;for(int i=0; i<Nsymbols; i++){if(frequency[i])Qtree.push(new Hnode( (char) i, frequency[i], NULL, NULL));}while(Qtree.size()>1){//不断定义Hnode* Hnode* lc = Qtree.top();Qtree.pop();Hnode* rc =Qtree.top();Qtree.pop();Hnode* parents = new Hnode((char) 256, lc->fre + rc->fre, lc,rc);Qtree.push(parents);//最后一次是把根节点push进Qtree中}return Qtree.top();//最后返回的是Haffuman树的根节点}void Huffman_coding(Hnode* root, Huff_code& curcode){if (root->isLeaf()){Huff_dic[root->data]=curcode;return;}Huff_code lcode = curcode;//定义左孩子的编码;Huff_code rcode = curcode;//定义右孩子的编码;lcode.push_back(false);rcode.push_back(true);Huffman_coding(root->left, lcode);Huffman_coding(root->right, rcode);}int main(){int freq[Nsymbols] = {0};char* c = "compress this is the string  into Huffman code";while(*c != '\0')freq[*c++]++ ;Hnode *root = bulidHtree(freq);Huff_code nullcode;nullcode.clear();Huffman_coding(root, nullcode);for(map<char, Huff_code>::iterator it = Huff_dic.begin(); it!=Huff_dic.end(); it++){cout<<(*it).first<<'\t';copy(it->second.begin(), it->second.end(), ostream_iterator<bool>(cout));cout<<endl;}}

 

 

Referance:

1. http://blog.csdn.net/abcjennifer/article/details/8020695

2. http://www.binaryessence.com/dct/en000042.htm
3. http://zh.wikipedia.org/zh/%E9%A6%99%E5%86%9C-%E8%8C%83%E8%AF%BA%E7%BC%96%E7%A0%81#.E5.93.88.E5.A4.AB.E6.9B.BC.E7.AE.97.E6.B3.95
4. http://www.stringology.org/DataCompression/sh/index_en.html
原创粉丝点击