zoukankan      html  css  js  c++  java
  • deflate树与deflate编码

    关于deflate树,能搜到的资料非常少,这个概念来自gzip的压缩算法,是由huffman树转变过来的。这里简单记录下deflate树的生成过程以及deflate编码。

    假设以5 8 9 10 14 15,建立一颗huffman树,可以是这个样子的:

                      61
               /            
              27              34
          /              /        
         14       13     15        19
                /               /   
               5     8          9    10

     也可以交换任意结点的两棵子树

                     61
               /            
              34              27                           
          /              /         
         15      19      14        13
               /                 /   
              9   10             5    8

    交换的过程虽然会改变叶子结点的huffman编码,但是,不会改变huffman树的带权路径和,也不会改变每个叶子结点的编码长度。基于这一点,我们可以做个更特殊的变换,每一层,让非叶子结点排在右边,叶子结点排在非叶子结点的左边。上面这棵树的变换之后如下:

                      61
               /            
              34              27                           
          /              /         
         15        14    19         13
                       /         /   
                       9   10     5    8
    经过变换后,上面这颗树就称为deflate树。同样,deflate树虽然改变了结点的huffman编码,但是没有改变每个元素的编码长度。在gzip压缩中的语义就是没有改变压缩率。
    上面的变化用语言表达起来不好理解,再用一个例子说明:
    假设下面是一个huffman树:
                      A
               /             
              B              C                           
          /              /      
          D        E      F      G
                 /     /   
                 G   H I    J
               /                    
               K   L                        
                 /  
                 M  N 
    转化为deflate之后,如下:
               A
               /             
              B              C                           
          /              /         
          D        G      F          E
                       /          /   
                       I   J      H    G
                                     /                    
                                     K   L                        
                                       /  
                                       M  N
    那么,转换为deflate树有什么好处呢?
    这涉及到码表的记录。所谓的码表就是元素及其对应的编码。
    先看下正常huffman编码下码表的记录
    还是以5 8 9 10 14 15为集合,以下面这颗huffman树为例:
                     61
               /            
              34              27                           
          /              /         
         15        19     14         13
                  /                /   
                 9   10            5    8
    假设走左为0,走右为1,那么码表就是:
    15          14              9             10            5            8
    00          10             010         011           110        111
    为了能够解码,我们必须把这个码表记录下来。
     
    再看下转换为deflate树后,如何记录
    上面这颗树转换后如下:
                     61
               /            
              34              27                           
          /              /         
         15        14    19         13
                        /         /   
                        9   10     5    8
    假设还是走左为0,走右为1。转换后元素的编码改变了,码表应该如下:
    15          14              9             10            5            8
    00          10             100         101           110        111
    虽然元素的编码变化了,但不要紧,只要我们记录如上这个码表,还是能把数据还原的。
    前边说过,deflate虽然改变了编码,但是每个元素的编码长度是不变的,这个时候,可以只记录每个元素的编码长度,就可以在解码的时候把数据还原。现在,码表这么记录,每一层,从左往右记录叶子结点的编码长度,层次按从上到下。先记录第2层(根节点为第0层)的两个叶子,再记录第三次的4个叶子,码表如下:
    15          14                 9         10            5            8
     2            2                 3           3             3            3
    先别管如何根据这个码表解码,先对比下这两种记录法,会发现,下面这种码表记录要比上面的码表记录节省比特,2的二进制位10   ,  3的二进制位11   ,总的比特位6*2=12。
    而上边的编码总长度为2+2+3+3+3+3=16(15、14的编码长度2,9、10、5、8的编码长度为3)。这并不是偶然,因为一个元素的编码的长度(10的编码长度为3)所占的二进制比特位(10的编码长度3,占二进制2位)肯定小于等于编码所占的长度(10的编码长度3)。
    这就是记录码长的好处,为什么要这么计较这一丁点的比特呢,要知道,deflate树是用于压缩算法的,而且这样做并不复杂,何乐而不为?

    现在再来说一下,有了这个码表如何解码,解码是编码的逆过程,所以,先看deflate树的编码

    deflate树,编码方式为:

    第n层的最左边的叶子结点的编码=((第n-1层的最左边的叶子结点的编码 )+  (第n-1层的叶子结点数))<< 1 。

    第n层,后一个叶子结点的编码 = 前一个叶子结点的编码+1

    还以下面这颗树为例:

                     61
               /            
              34              27                           
          /              /         
         15        14    19         13
                       /         /   
                      9   10     5    8
    15的编码为00
    那么9的编码 = (上一层最左边的叶子结点15的编码+上一层的叶子结点数2)<<1
                     =   (00 + 10)<<1
                     =    100
    10的编码 = 9的编码+1 = 101
    5的编码  = 10的编码+1 = 110
    8的编码  = 5的编码+1 = 111
     
    现在可以说解码过程了,码表先搬下来:
    15          14                 9         10            5            8
     2            2                 3           3             3            3
    由于这个码表的记录方法是每层叶子结点从左到右,并且层次从上到下的方式,而且,会发现,编码长度就是叶子所在的层次(假设根节点为第0层)。所以,第二层开始出现了第一个叶子结点,第一个叶子结点一定是一直往左的。那么根据编码规则15的编码就是00,14的编码是01,9的编码是(00+2)<<1 = 100...
     
    这就是deflate树与deflate编码。事实上,在gzip中,deflate树的码表并不是这么记录,但deflate树的编码和解码思想是这样的。上面的码表了记录元素及其对应的码长,但在gzip中,为了更好压缩效果,并不会记录元素,而是直接记录元素的编码长度,用一个长度序列来表示码表。如果想了解其实现,应该去看看gzip的源码,gzip的源码非常精彩,极客思想无处不在,简直让人叹为观止。
  • 相关阅读:
    彻底搞清分库分表(垂直分库,垂直分表,水平分库,水平分表)
    linux服务器上tcp有大量time_wait状态的解决方法和原因解释
    mysql,既可以自己的字段相乘,也可以乘固定的字段
    vscode 滚动设置字体大小
    nodejs 定时任务 node-schedule 库
    Node.js中的环境变量
    js 打印错误堆栈
    springboot 返回的json中忽略null属性值,不传递
    idea跳转到指定行列快捷键
    Spring boot + MyBatis返回map中null值处理
  • 原文地址:https://www.cnblogs.com/zengzy/p/5156130.html
Copyright © 2011-2022 走看看