霍夫曼树的存储和重建

时间:2013-02-26 04:22:17

标签: c++ tree huffman-code

脱水霍夫曼树的最佳方法是什么,脱水我的意思是给出一个霍夫曼树,每个叶子中的字符,你如何有效地存储这棵树的结构,然后重建它。

采取以下树:

---------------garbage------
 -------------/-------\------
 ------------A-------garbage-
 --------------------/-----\-
 -------------------B-------C-

一个想法可能是在每个级别存储符号,然后使用此信息重建树。 在这种情况下: A1B2C2。 那么我怎样才能首先获得关卡,并将每个关卡与角色联系起来。

2 个答案:

答案 0 :(得分:4)

您几乎肯定不需要存储树本身。你可以做到,它不应该占用你认为它所做的空间,但通常不是必需的。

如果您的霍夫曼代码是规范的,则只需存储每个符号的位长度,因为这是生成规范编码所需的所有信息。这是每个符号相对较少的位数,因此应该相当紧凑。您还可以进一步压缩该信息(请参阅answer中的Aki Suihkonen)。

当然,代码的位长度与树深度基本相同,所以我认为这大致是你所要求的。重要的部分是知道如何构建规范代码,给定长度 - 它不一定与遍历树所产生的代码相同。您可以从此重新生成 a 树,但它不一定是您开始使用的树 - 但通常您不需要树而不是首先确定代码长度。

生成规范代码的算法非常简单:

  1. 获取您想要生成代码的所有符号,首先按代码长度(最短的第一个)排序,然后按符号本身排序。
  2. 以零长度代码开头。
  3. 如果下一个符号需要的代码比当前代码中的位数多,则在代码的右侧(最低有效位)添加零,直到它的长度合适为止。
  4. 将代码与当前符号相关联,并递增代码。
  5. 循环回(3),直到您生成了所有符号。
  6. 取字符串“banana”。显然,使用了3个符号,'b','a'和'n',分别为1,3和2。

    所以树可能看起来像这样:

        *
       / \
      *   a
     / \
    b   n
    

    天真地,这可以给出代码:

    a = 1
    b = 00
    n = 01
    

    但是,如果您只是使用位长作为规范代码生成的输入,那么您将产生以下结果:

    a = 0
    b = 10
    n = 11
    

    它是一个不同的代码,但显然它会产生相同长度的压缩输出。此外,您只需存储代码长度即可重现代码。

    所以你只需要存储一个序列:

    0... 1 2 0... 2 0...
    

    其中“...”表示容易压缩的重复,并且值都非常小(每个可能只有4位 - 并注意到符号根本不存储)。这种表现形式非常紧凑。

    如果你真的必须存储树本身,一种技术是遍历树并存储一个位以指示节点是内部还是叶子,然后存储叶节点,存储符号代码。这对于不包含每个符号的树来说相当紧凑,即使对于相当完整的树也不会太糟糕。最坏的情况是你所有符号的总大小,加上你可以拥有节点的单个位数。对于标准的8位字节流,这将是320字节(代码为256字节,树结构本身为511位)。

    方法是从根节点开始,对于每个节点:

    • 如果节点是父节点,则输出0,然后输出左侧和右侧的子节点。
    • 如果节点是叶子,则输出1,然后输出符号

    要重建,执行类似的递归过程,但显然会读取数据并选择是否递归创建子项,或者在适当时读取符号。

    对于上面的示例,树的比特流将类似于:

    0, 0, 1, 'b', 1, 'n', 1, 'a'
    

    树的5位,加上符号的3个字节,最多可存储4个字节。但是,当您添加更多符号时,它会迅速增长,而存储代码长度则不会。

答案 1 :(得分:2)

zlib specification解释了存储霍夫曼树只需要每个符号的比特长度。例如。如果为A = 101,B = 111,C = 110,D = 01构造树,则将简单地计算比特长度并从长度重新生成树,使得关键字将是连续的 - > A = 101,B = 110,C = 111,D = 01。 (或以下代码产生的内容)

设置bl_count[2]=1, bl_count[3]=3并迭代:

code = 0;   // From z-lib specification, RFC 1951
bl_count[0] = 0;
for (bits = 1; bits <= MAX_BITS; bits++) {
    code = (code + bl_count[bits-1]) << 1;
    next_code[bits] = code;
}

当最大符号长度<16时,每个符号最多需要4比特来存储这些长度:3,3,3,2 == 0011 0011 0011 0010;但是,zlib / deflate做得更好 - 运行长度使用转义符号编码这些符号,如16 ==运行3,17:运行4等,以进一步压缩符号长度流。此外,RLE采用零长度的情况,即缺少字符。