Huffman 编码压缩算法

Huffman 编码压缩算法

前两天发布那个rsync算法后,想看看数据压缩的算法,知道一个经典的压缩算法Huffman算法。相信大家应该听说过 David Huffman 和他的压缩算法—— Huffman Code,一种通过字符出现频率,Priority Queue,和二叉树来进行的一种压缩算法,这种二叉树又叫Huffman二叉树 —— 一种带权重的树。从学校毕业很长时间的我忘了这个算法,但是网上查了一下,中文社区内好像没有把这个算法说得很清楚的文章,尤其是树的构造,而正好看到一篇国外的文章《A Simple Example of Huffman Code on a String》,其中的例子浅显易懂,相当不错,我就转了过来。注意,我没有对此文完全翻译。

我们直接来看示例,如果我们需要来压缩下面的字符串:

 “beep boop beer!” 

首先,我们先计算出每个字符出现的次数,我们得到下面这样一张表 :

字符 次数
‘b’ 3
‘e’ 4
‘p’ 2
‘ ‘ 2
‘o’ 2
‘r’ 1
‘!’ 1


然后,我把把这些东西放到Priority Queue中(用出现的次数据当 priority),我们可以看到,Priority Queue 是以Prioirry排序一个数组,如果Priority一样,会使用出现的次序排序:下面是我们得到的Priority Queue:

接下来就是我们的算法——把这个Priority Queue 转成二叉树。我们始终从queue的头取两个元素来构造一个二叉树(第一个元素是左结点,第二个是右结点),并把这两个元素的priority相加,并放回Priority中(再次注意,这里的Priority就是字符出现的次数),然后,我们得到下面的数据图表:

同样,我们再把前两个取出来,形成一个Priority为2+2=4的结点,然后再放回Priority Queue中 :

继续我们的算法(我们可以看到,这是一种自底向上的建树的过程):

最终我们会得到下面这样一棵二叉树:

此时,我们把这个树的左支编码为0,右支编码为1,这样我们就可以遍历这棵树得到字符的编码,比如:‘b’的编码是 00,’p’的编码是101, ‘r’的编码是1000。我们可以看到出现频率越多的会越在上层,编码也越短,出现频率越少的就越在下层,编码也越长

最终我们可以得到下面这张编码表:

字符 编码
‘b’ 00
‘e’ 11
‘p’ 101
‘ ‘ 011
‘o’ 010
‘r’ 1000
‘!’ 1001


这里需要注意一点,当我们encode的时候,我们是按“bit”来encode,decode也是通过bit来完成,比如,如果我们有这样的bitset “1011110111″ 那么其解码后就是 “pepe”。所以,我们需要通过这个二叉树建立我们Huffman编码和解码的字典表。

这里需要注意的一点是,我们的Huffman对各个字符的编码是不会冲突的,也就是说,不会存在某一个编码是另一个编码的前缀,不然的话就会大问题了。因为encode后的编码是没有分隔符的。

于是,对于我们的原始字符串  beep boop beer!

其对就能的二进制为 : 0110 0010 0110 0101 0110 0101 0111 0000 0010 0000 0110 0010 0110 1111 0110 1111 0111 0000 0010 0000 0110 0010 0110 0101 0110 0101 0111 0010 0010 0001

我们的Huffman的编码为: 0011 1110 1011 0001 0010 1010 1100 1111 1000 1001

从上面的例子中,我们可以看到被压缩的比例还是很可观的。

作者给出了源码你可以看看( C99标准) Download the source files

(全文完)

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

好烂啊有点差凑合看看还不错很精彩 (24 人打了分,平均分: 4.17 )
Loading...

Huffman 编码压缩算法》的相关评论

  1. @stevenliu
    出现频率越高,压缩后越小,如上边的 3个“b” 8bit变成了2bit,相当于减小了3*(8-2)=18bit,还是相当可观的吧,特别是当文章很长的时候。

  2. g0t3n :
    @Walkerinwind
    两位果然厉害,我表示看了足足半个小时才看懂,而且编码的唯一性那里还是不是很理解….
    btw,感觉如果是小的程序,例如只有唯一字符的a-z的26个字符的文件中,编码后文件得加上编码表,恐怕比源文件还要大了吧

    所以一些压缩软件才自备了一些简单的哈夫曼树,这样就不必写入到压缩包里了。

  3. @陈皓
    那一篇确实不适合搞计算机的人来读,它也不是着重讲算法本身,而是努力通过以较自然的方式阐述算法是如何被发现的,来帮助人记住如何去证明这个算法是输入为特定统计模型下的最优算法。

  4. 不同的编码适应不同的信源.霍夫曼适合信源平率差别较大的信源.所以,使用霍夫曼压缩的时候,不是单单是要用.还先要对信源做变换,使其变得适合用霍夫曼编码.例如对于图像压缩,先做dct.再压….别来不来不分析信源就上编码方案.对于一串串ABC的,可以用字典编码试试,没做过实验,不知道具体效果怎样.不过效果应该可以.

  5. 算法摘要:1,用叶子结点描述字符以保证每个字符的编码不会是其他字符编码的前缀;2,让出现频率高的字符使用更短的编码。

  6. 哈夫曼,遥想当年,大3大4的专业课里至少有3门把它当重点来考,简直就是送分。想要知道算法,随便找本教材来回顾一下就行了。

  7. lz的表达能力真好。我之前也写过一个huffman算法的介绍文章,还是看着Huffman的论文写的,结果怎么都表述不清楚这个二叉树是怎么构造的。
    可能我理解的还不够透彻吧,其实我觉得我已经理解了。

  8. 请教一下:

    ‘o’ 和 ‘ ‘ 那对的父节点, 就是 3 4 4 4 那张图

    为什么不是放在 空 和 ‘p’ 的 父节点 与 ‘e’ 的中间

    #如果Priority一样,会使用出现的次序排序

    是不是 空 节点,看成没有出现,排序最后?
    但是如果这样,那上一张的时候 2 2 3 4 4
    空 和 ‘p’ 的父节点 应该放在 ‘e’ 后面才对啊?

    求高手解释,谢谢,全篇只有这一处想不明白

  9. 在第一张图下方这句”然后,我把把这些“,个人感觉多了一个“把”,读起来觉得有点拗口。
    感觉就是少了很多东西,比如优先队列的一点操作就是在选择两个最小之后优先队列变化了,这点未写清。
    编码和解码写得有点少吧。呵呵

  10. Pingback: 性能调优攻略
  11. Pingback: K-Means 算法
  12. ”出现频率越多的会越在上层,编码也越短,出现频率越少的就越在下层,编码也越长。“
    其实算法没怎么看懂,树是怎么出来的,
    但【出现越多,给它的编码越少(就越省空间)】,
    是否可以简化算法,主要为统计字符出现次数,然后直接给它赋【编码】

  13. 皓哥,最近也在研究zip等,问一下,你研究过zip加密么?我只知道它是用AES加密的(标准里),但是它的密码扩展算法,还有一些key之类的,知道是怎么定义的?能没有类似的文章推荐下呢?谢了

回复 康杜 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注