哈夫曼算法原理



Wikipedia上面说的非常清楚了,这里我就不再赘述,直接贴过来了。


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

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

演示样例

Huffman Algorithm


符号 A B C D E
计数 15 7 6 6 5
概率 0.38461538 0.17948718 0.15384615 0.15384615 0.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
代码 0 100 101 110 111

    Entropy:



Pseudo-code


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




哈夫曼算法实现



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

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

  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)形參中都是指针类型的是不能够的。我们仅仅能再建一个类,用当中的重载()操作符作为优先队列的比較函数。

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

  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:






关于Compression很多其它的学习资料将继续更新,敬请关注本博客和新浪微博Sophia_qing







huffman编码——原理与实现的更多相关文章

  1. [老文章搬家] 关于 Huffman 编码

    按:去年接手一个项目,涉及到一个一个叫做Mxpeg的非主流视频编码格式,编解码器是厂商以源代码形式提供的,但是可能代码写的不算健壮,以至于我们tcp直连设备很正常,但是经过一个UDP数据分发服务器之后 ...

  2. 哈夫曼(Huffman)编码

    哈夫曼编码(Huffman Coding)是一种非常经典的编码方式,属于可变字长编码(VLC)的一种,通过构造带权路径长度最小的最优二叉树以达到数据压缩的目的.哈弗曼编码实现起来也非常简单,在实际的笔 ...

  3. Huffman编码实现文件的压缩与解压缩。

    以前没事的时候写的,c++写的,原理很简单,代码如下: #include <cstdio> #include <cstdlib> #include <iostream&g ...

  4. Huffman编码实现压缩解压缩

    这是我们的课程中布置的作业.找一些资料将作业完毕,顺便将其写到博客,以后看起来也方便. 原理介绍 什么是Huffman压缩 Huffman( 哈夫曼 ) 算法在上世纪五十年代初提出来了,它是一种无损压 ...

  5. 基于二叉树和双向链表实现限制长度的最优Huffman编码

    该代码採用二叉树结合双向链表实现了限制长度的最优Huffman编码,本文代码中的权重所有採用整数值表示.http://pan.baidu.com/s/1mgHn8lq 算法原理详见:A fast al ...

  6. Huffman编码实验

    一. 实验目的 熟练掌握哈夫曼树的建立和哈夫曼编码的算法实现. 二. 实验内容 根据哈夫曼编码的原理,编写一个程序,在用户输入结点权值的基础上求赫夫曼编码,并能把给定的编码进行译码. 三. 实验要求 ...

  7. Huffman编码

    #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstri ...

  8. 【数据压缩】Huffman编码

    1. 压缩编码概述 数据压缩在日常生活极为常见,平常所用到jpg.mp3均采用数据压缩(采用Huffman编码)以减少占用空间.编码\(C\)是指从字符空间\(A\)到码字表\(X\)的映射.数据压缩 ...

  9. 优先队列求解Huffman编码 c++

    优先队列小析      优先队列的模板: template <class T, class Container = vector<T>,class Compare = less< ...

随机推荐

  1. iOS开发UI篇——九宫格坐标计算

    一.要求 完成下面的布局 二.分析 寻找左边的规律,每一个uiview的x坐标和y坐标. 三.实现思路 (1)明确每一块用得是什么view (2)明确每个view之间的父子关系,每个视图都只有一个父视 ...

  2. 最全ASCLL码

    结果 描述 实体编号   space ! exclamation mark ! " quotation mark " # number sign # $ dollar sign $ ...

  3. C# trace debug TraceListener调试信息详解

    在C#编程中,可能要碰到把调试信息输出的问题,我们可以自己把信息显示在某个控件上,但是MS自己提供了一套机制帮助我们输出一些调试信息,这些信息有助于我们判断程序的走向,不用自己再去额外写调试代码了. ...

  4. Qt中绘图坐标QPainter,Viewport与Window的关系

    在Qt中常常要自己重载一些paintEvent函数,这个时候往往忽略了两个很关键的API,那就是setViewport和setWindow. Viewport,顾名思义,反应的是物理坐标,就是你实际想 ...

  5. jquery 文本框聚焦文字删除

    做作业需要,自己写了一个,写的很烂. $(function() { $("#search_input").addClass("before_focus");/* ...

  6. text-overflow:ellipsis 的应用(转载)

    关键字: text-overflow:ellipsis 语法:text-overflow : clip | ellipsis 取值: clip :默认值 .不显示省略标记(...),而是简单的裁切. ...

  7. 实验二:基于mykernel实现的时间片轮转调度

    原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 如果我写的不好或者有误的地方请留言 ...

  8. The executable was signed with invalid entitlements.

    如图,出现这个的原因是   配置文件(provisioning  profile)和  app 授权文件中的   entitlements(授权)  不匹配 具体应该从  配置文件  和证书的对应 问 ...

  9. PostBack与IsPostBack区别

    这涉及到aspx的页面回传机制的基础知识 postback是回传 即页面在首次加载后向服务器提交数据,然后服务器把处理好的数据传递到客户端并显示出来,就叫postback, ispostback只是一 ...

  10. ubuntu下MySQL安装配置及基本操作

    在linux下安装方法: 分为四种:一: 直接用软件仓库自动安装(如:ubuntu下,sudo apt-get install mysql-server; Debain下用yum安装): 二:官网下载 ...