繁体   English   中英

霍夫曼树的存储和重建

[英]Storing and reconstruction of Huffman tree

使霍夫曼树脱水的最佳方法是什么,通过脱水我指的是给霍夫曼树,以及每片叶子中的字符,如何才能有效地存储此树的结构并随后对其进行重构。

采取下面的树:

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

一个想法可能是在每个级别上存储符号,然后使用此信息来重构树。 在这种情况下:A1B2C2。 因此,我如何首先获得关卡,并将每个关卡与角色相关联。

您几乎可以肯定不需要存储树本身。 您可以这样做,并且不应占用您认为需要的空间,但是通常没有必要。

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

自然,代码的位长与树的深度基本相同,所以我认为这大致就是您要问的。 重要的部分是在给定长度的情况下,知道如何构建规范代码-它不一定与遍历树所产生的代码相同。 你可以从这个再生棵树,但它不一定是你开始与树-但是通常你不需要其他的树,而不是确定在首位的代码长度。

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

  1. 取所有要为其生成代码的符号,先按代码长度(最短的最短)排序,然后再按符号本身排序。
  2. 从零长度代码开始。
  3. 如果下一个符号所需的位数比代码中当前的位数多,请在代码的右边(最低有效位)添加零,直到长度合适为止。
  4. 将代码与当前符号关联,然后增加代码。
  5. 循环回到(3),直到生成了所有符号。

取字符串“香蕉”。 显然,使用了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个字节的存储空间。 但是,当您添加更多符号时,它将迅速增长,而存储代码长度则不会。

zlib规范解释说,存储霍夫曼树只需要每个符号的位长。 例如,如果有人为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的长度为零,即缺少字符。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM