哈夫曼算法原理

1952年, David A. Huffman提出了一个不同的算法,这个算法可以为任何的可能性提供出一个理想的树。香农-范诺编码(Shanno-Fano)是从树的根节点到叶子节点所进行的的编码,哈夫曼编码算法却是从相反的方向,暨从叶子节点到根节点的方向编码的。

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

示例


Huffman Algorithm


符号 A B C D E
计数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比特。

字符 A B C D E
代码0100101110111

     Entropy:



Pseudo-code


 1:  begin
 2:     count frequencies of single characters (source units)
 3:     output(frequencies using Fibonacci Codes of degree 2)
 4:     sort them to non-decreasing sequence
 5:     create a leaf node (character, frequency c, left son = NULL, right son = NULL) 
 6:  	   of the tree for each character and put nodes into queue F
 7:     while (|F|>=2) do 
 8:      begin
 9:        pop the first two nodes (u1, u2) with the lowest 
10:  	      frequencies from sorted queue
11:        create a node evaluated with sum of the chosen units, 
12:  	      successors are chosen units (eps, c(u1)+c(u2), u1, u2)
13:        insert new node into queue
14:      end
15:     node evaluate with way from root to leaf node (left son 1, right son 0)
16:     create output from coded intput characters
17:  end




哈夫曼算法实现



实现的时候我们用vector<bool>记录每个char的编码;用map<char,vector<bool>>表示整个字典;
就得到了下面的代码(下面有两个,一对一错):

先放出来这个错的,以示警戒

[cpp]  view plain  copy
  1. /************************************************************************/  
  2. /*  File Name: Huffman.cpp 
  3. *       @Function: Lossless Compression 
  4.         @Author: Sophia Zhang 
  5.         @Create Time: 2012-9-26 10:40 
  6.         @Last Modify: 2012-9-26 11:10 
  7. */  
  8. /************************************************************************/  
  9.   
  10. #include"iostream"  
  11. #include "queue"  
  12. #include "map"  
  13. #include "string"  
  14. #include "iterator"  
  15. #include "vector"  
  16. #include "algorithm"  
  17. using namespace std;  
  18.   
  19. #define NChar 8 //suppose use at most 8 bits to describe all symbols  
  20. #define Nsymbols 1<<NChar //can describe 256 symbols totally (include a-z, A-Z)  
  21. typedef vector<bool> Huff_code;//8 bit code of one char  
  22. map<char,Huff_code> Huff_Dic; //huffman coding dictionary  
  23.   
  24. class HTree  
  25. {  
  26. public :  
  27.     HTree* left;  
  28.     HTree* right;  
  29.     char ch;  
  30.     int weight;  
  31.   
  32.     HTree(){left = right = NULL; weight=0;}  
  33.     HTree(HTree* l,HTree* r,int w,char c){left = l; right = r;  weight=w;   ch=c;}  
  34.     ~HTree(){delete left; delete right;}  
  35.     int Getweight(){return weight?weight:left->weight+right->weight;}  
  36.     bool Isleaf(){return !left && !right; }  
  37.     bool operator < (const HTree tr) const  
  38.     {  
  39.         return tr.weight < weight;  
  40.     }  
  41. };  
  42.   
  43. HTree* BuildTree(int *frequency)  
  44. {  
  45.     priority_queue<HTree*> QTree;  
  46.   
  47.     //1st level add characters  
  48.     for (int i=0;i<Nsymbols;i++)  
  49.     {  
  50.         if(frequency[i])  
  51.             QTree.push(new HTree(NULL,NULL,frequency[i],(char)i));            
  52.     }  
  53.   
  54.     //build  
  55.     while (QTree.size()>1)  
  56.     {  
  57.         HTree* lc  = QTree.top();  
  58.         QTree.pop();  
  59.         HTree* rc = QTree.top();  
  60.         QTree.pop();  
  61.   
  62.         HTree* parent = new HTree(lc,rc,parent->Getweight(),(char)256);  
  63.         QTree.push(parent);  
  64.     }  
  65.     //return tree root  
  66.     return QTree.top();  
  67. }  
  68.   
  69. void Huffman_Coding(HTree* root, Huff_code& curcode)  
  70. {  
  71.     if(root->Isleaf())  
  72.     {  
  73.         Huff_Dic[root->ch] = curcode;  
  74.         return;  
  75.     }  
  76.     Huff_code& lcode = curcode;  
  77.     Huff_code& rcode = curcode;  
  78.     lcode.push_back(false);  
  79.     rcode.push_back(true);  
  80.   
  81.     Huffman_Coding(root->left,lcode);  
  82.     Huffman_Coding(root->right,rcode);  
  83. }  
  84.   
  85. int main()  
  86. {  
  87.     int freq[Nsymbols] = {0};  
  88.     char *str = "this is the string need to be compressed";  
  89.   
  90.     //statistic character frequency  
  91.     while (*str!='\0')  
  92.         freq[*str++]++;  
  93.   
  94.     //build tree  
  95.     HTree* r = BuildTree(freq);  
  96.     Huff_code nullcode;  
  97.     nullcode.clear();  
  98.     Huffman_Coding(r,nullcode);  
  99.   
  100.     for(map<char,Huff_code>::iterator it = Huff_Dic.begin(); it != Huff_Dic.end(); it++)  
  101.     {  
  102.         cout<<(*it).first<<'\t';  
  103.         Huff_code vec_code = (*it).second;  
  104.         for (vector<bool>::iterator vit = vec_code.begin(); vit!=vec_code.end();vit++)  
  105.         {  
  106.             cout<<(*vit)<<endl;  
  107.         }  
  108.     }  
  109. }  


上面这段代码,我运行出来不对。在调试的时候发现了一个问题,就是QTree优先队列中的排序出了问题,说来也是,上面的代码中,我重载小于号是对HTree object做的;而实际上我建树时用的是指针,那么优先级队列中元素为指针时该怎么办呢?

那我们将friend bool operator >(Node node1,Node node2)修改为friend bool operator >(Node* node1,Node* node2),也就是传递的是Node的指针行不行呢?


答案是不可以,因为根据c++primer中重载操作符中讲的“程序员只能为类类型或枚举类型的操作数定义重载操作符,在把操作符声明为类的成员时,至少有一个类或枚举类型的参数按照值或者引用的方式传递”,也就是说friend bool operator >(Node* node1,Node* node2)形参中都是指针类型的是不可以的。我们只能再建一个类,用其中的重载()操作符作为优先队列的比较函数。




就得到了下面正确的代码:


[cpp]  view plain  copy
  1. /************************************************************************/  
  2. /*  File Name: Huffman.cpp 
  3. *       @Function: Lossless Compression 
  4.         @Author: Sophia Zhang 
  5.         @Create Time: 2012-9-26 10:40 
  6.         @Last Modify: 2012-9-26 12:10 
  7. */  
  8. /************************************************************************/  
  9.   
  10. #include"iostream"  
  11. #include "queue"  
  12. #include "map"  
  13. #include "string"  
  14. #include "iterator"  
  15. #include "vector"  
  16. #include "algorithm"  
  17. using namespace std;  
  18.   
  19. #define NChar 8 //suppose use 8 bits to describe all symbols  
  20. #define Nsymbols 1<<NChar //can describe 256 symbols totally (include a-z, A-Z)  
  21. typedef vector<bool> Huff_code;//8 bit code of one char  
  22. map<char,Huff_code> Huff_Dic; //huffman coding dictionary  
  23.   
  24. /************************************************************************/  
  25. /* Tree Class elements: 
  26. *2 child trees 
  27. *character and frequency of current node 
  28. */  
  29. /************************************************************************/  
  30. class HTree  
  31. {  
  32. public :  
  33.     HTree* left;  
  34.     HTree* right;  
  35.     char ch;  
  36.     int weight;  
  37.   
  38.     HTree(){left = right = NULL; weight=0;ch ='\0';}  
  39.     HTree(HTree* l,HTree* r,int w,char c){left = l; right = r;  weight=w;   ch=c;}  
  40.     ~HTree(){delete left; delete right;}  
  41.     bool Isleaf(){return !left && !right; }  
  42. };  
  43.   
  44. /************************************************************************/  
  45. /* prepare for pointer sorting*/  
  46. /*because we cannot use overloading in class HTree directly*/  
  47. /************************************************************************/  
  48. class Compare_tree  
  49. {  
  50. public:  
  51.     bool operator () (HTree* t1, HTree* t2)  
  52.     {  
  53.         return t1->weight> t2->weight;  
  54.     }  
  55. };  
  56.   
  57. /************************************************************************/  
  58. /* use priority queue to build huffman tree*/  
  59. /************************************************************************/  
  60. HTree* BuildTree(int *frequency)  
  61. {  
  62.     priority_queue<HTree*,vector<HTree*>,Compare_tree> QTree;  
  63.   
  64.     //1st level add characters  
  65.     for (int i=0;i<Nsymbols;i++)  
  66.     {  
  67.         if(frequency[i])  
  68.             QTree.push(new HTree(NULL,NULL,frequency[i],(char)i));            
  69.     }  
  70.   
  71.     //build  
  72.     while (QTree.size()>1)  
  73.     {  
  74.         HTree* lc  = QTree.top();  
  75.         QTree.pop();  
  76.         HTree* rc = QTree.top();  
  77.         QTree.pop();  
  78.   
  79.         HTree* parent = new HTree(lc,rc,lc->weight+rc->weight,(char)256);  
  80.         QTree.push(parent);  
  81.     }  
  82.     //return tree root  
  83.     return QTree.top();  
  84. }  
  85.   
  86. /************************************************************************/  
  87. /* Give Huffman Coding to the Huffman Tree*/  
  88. /************************************************************************/  
  89. void Huffman_Coding(HTree* root, Huff_code& curcode)  
  90. {  
  91.     if(root->Isleaf())  
  92.     {  
  93.         Huff_Dic[root->ch] = curcode;  
  94.         return;  
  95.     }  
  96.     Huff_code lcode = curcode;  
  97.     Huff_code rcode = curcode;  
  98.     lcode.push_back(false);  
  99.     rcode.push_back(true);  
  100.   
  101.     Huffman_Coding(root->left,lcode);  
  102.     Huffman_Coding(root->right,rcode);  
  103. }  
  104.   
  105. int main()  
  106. {  
  107.     int freq[Nsymbols] = {0};  
  108.     char *str = "this is the string need to be compressed";  
  109.   
  110.     //statistic character frequency  
  111.     while (*str!='\0')  
  112.         freq[*str++]++;  
  113.   
  114.     //build tree  
  115.     HTree* r = BuildTree(freq);  
  116.     Huff_code nullcode;  
  117.     nullcode.clear();  
  118.     Huffman_Coding(r,nullcode);  
  119.   
  120.     for(map<char,Huff_code>::iterator it = Huff_Dic.begin(); it != Huff_Dic.end(); it++)  
  121.     {  
  122.         cout<<(*it).first<<'\t';  
  123.         std::copy(it->second.begin(),it->second.end(),std::ostream_iterator<bool>(cout));  
  124.         cout<<endl;  
  125.     }  
  126. }  








Reference:
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值