详解Huffman压缩原理和c++代码实现

写在前面

    Huffman压缩原理其实挺好理解的,我用java很快就写好了。然后用c++写,一开始我是这么想的:c++偏底层,应该对二进制串文件的读写会更简单吧。

    不涉及到文件读写的部分确实很快就做好了,然后就被文件读写折磨。

    各种深夜痛哭... ...

    但还是值得的,学习了更多底层的知识。我对Huffman压缩基本掌握了。(本来想说完全掌握的,但,呵,生活。微笑微笑

    写这篇博客花了我很长时间,我完全尽力了。我尽可能详细地写了三部分,分别是Huffman原理、坑和c++代码实现。我举了一些例子,并且经过实际动手验证,还自行绘制了几幅图帮助理解。

    欢迎指错和讨论交流,也欢迎提问质疑,尽管能力有限,但我尽力解答。

目录

    一、Huffman压缩

        1、文件在计算机中存储形式

        2、Huffman压缩算法原理

        3、例子解析

    二、需要特别注意的坑

        1、windows的'\r\n'问题,c/c++可以用二进制方式读取来解决

        2、文件读取末尾问题

    三、Huffman的实现(上代码,本文c++版本,如果要java版本请私聊我)
        编码篇

        1、统计频率

        2、建立Huffman树

        3、获取Huffman编码表

        4、编码

        译码篇

        1、获取Huffman译码表

        2、译码


正文

  一、Huffman压缩

        1、文件在计算机中存储形式

        在开始实现之前,我们要了解下计算机底层的编解码。

       计算机只认0与1,一切文件最终的存储形式都是0、1串。文本、图片、视频等文件都是通过一定的协议进行编解码,而    这些协议及其转化由各种各样的软件(音频软件、画图软件、记事本等)实现。 下图是一张png图片文件在计算机中存储形式, 可以通过Binary Viewer 软件进行查看。     

                      

        文件的存储有定长存储和不定长存储,定长存储就是int、char、byte等类型会以固定的位数b进行存储。举个例子,我们新建txt文件,写入“4 中”然后敲回车换行,再写入“16”,保存。现在我们用Binary Viewer查看它在计算机中的存储。                      

       因为我们是用文本文件进行存储,所以记事本的编解码方式是将每一个字符对应的ASCII码写入文本文件,如果是0-127的只有8位,如果是中文这样的拓展字符则有16位。

     我们看图吧,第一个字节(8位)“00110100”转化为10进制为52, 查ASCII表为‘4’,接下来的第二个字节“00100000”代表空格,接下来的两个字节是‘中’,然后依次是‘\r’,‘\n’,‘1’,6’。这里特别强调下,Windows系统用‘\r\n’表示换行,而Liunx用‘\n’,Mac用‘\r’。为什么会有如此差异呢?感兴趣的自行百度,这个跟早期打印机有关,现在只是一个规则,并没有实际含义,但要特别特别注意 。我写Huffman的时候被这个坑了,具体我们后面再说。我强烈建议写压缩的时候用上查看二进制串的工具(比如Binary Viewer),有利于理解和debug。

        2、Huffman压缩算法原理

        我们上边提到了计算机的定长存储,其实我们也看到了存储‘4’这样的数字,计算机用了8位,那么我们能不能减少二进制串呢,从而来实现压缩?有很多压缩算法,他们主要是用新一套的编解码表来实现。而每次所用的编码表都是不同的,是依据压缩的文件来决定的。

        那Huffman是怎么做的呢?它先通过对要压缩的文件进行统计频率,比如在“65da as 美65a”中a-3(表示a出现3次),d-1,s-1,6-2,5-2,美-1,空格-2。Huffman采用不定长进行存储,频率高的对应的编码长度较短,频率低的对应的编码长度较长。但我们压缩后是要能解压的,假如有这样的一组编码“00101110”,Huffman压缩算法每次读取一位,直至找到在Huffman编码表中找到,然后去除这一串,接着重复以上操作,直至编码读取完毕。要实现这样的结果,我们要怎么创立Huffman编码表呢?以下是具体做法:(边看例子辅助理解)

    (1)先对压缩文件的字符进行频率统计,以“字符--频率”的形式存入某容器m

    (2) 在容器m中取出两个频率最小对应的字符,作为二叉树的两个叶子节点,并将频率和作为它们的根节点,同时将新结点存入容器m,将旧的两个结点踢出容器m。(容器m可以是优先队列)

    (3)重复(2),直到最后容器m中只有一个元素。

    (4) 将形成的二叉树的左节点标0,右节点标1。把从最上面的根节点到最下面的叶子节点途中遇到的0,1序列串起来,就得到了各个符号的编码。

        3、例子解析

    例子:有一串“cdbedfaabca”,进行Huffman编码和解码。

编码: (1)频率统计  f:1   e:1   d:2   c:2   b:2   a:5

            (2) f与   e作为叶子结点,其根节点为_2 。   此时,新的频率表为:_2   d:2   c:2   b:2   a:5

                      d

  • 25
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值