|
|
1
69
不要存储实际频率,解码时不需要它们。然而,你确实需要一棵真正的树。
要阅读,请执行以下操作:
叶子节点基本上是任何没有子节点的节点。 通过这种方法,您可以在编写输出之前计算出输出的确切大小,以确定收益是否足以证明所做的努力是合理的。这假设您有一个包含每个字符频率的键/值对字典,其中频率是实际出现的次数。
树大小计算考虑了叶子和非叶子节点,内联节点比字符少一个。 SIZE_OF_ONE_CHARACTER将是比特数,这两个比特数将给你我的树方法+编码数据将占用的总比特数。 PATH(c)是一个函数/表,它将生成从根到树中该字符的位路径。 这是一个C#风格的伪代码,它假设一个字符只是一个简单的字节。
要重新阅读:
这是一个特定示例的输出示例。
频率:
每个字符只有8位,因此树的大小将是10*5-1=49位。
因此,每个字符的路径如下(0为左,1为右):
因此,要计算输出大小:
编码字节之和为12+3+12+6+10=43位 将其与树中的49位相加,输出将为92位,即12个字节。与存储原始20个未编码字符所需的20*8字节相比,您将节省8个字节。 最终输出(包括开始的树)如下。流(A-E)中的每个字符都被编码为8位,而0和1只是一个位。流中的空间只是为了将树与编码数据分开,在最终输出中不占用任何空间。
对于您在评论中的具体示例AABCDEF,您将得到以下内容: 输入:AABCDEF 频率:
树:
路径:
树:001A1B001C1D01E1F=59位
由于原始数据是8位=56的7个字符,因此这些小数据块的开销太大。 |
|
|
2
9
如果你对树的生成有足够的控制,你可以让它做一个规范树(同样的方式 DEFLATE 例如,确实如此),这基本上意味着您在构建树时创建规则来解决任何不明确的情况。然后,就像DEFLATE一样,您实际需要存储的是每个字符的代码长度。
然后,您可以将它们存储为: 2, 3, 2, 3, 2
如果你想得到 真的 如果你已经熟悉哈夫曼编码,DEFLATE的RFC还不错: http://www.ietf.org/rfc/rfc1951.txt |
|
3
4
如果你正在对其进行分块,你可以测试为下一个卡盘存储树与为上一个块重用树一样有效,并将树的形状设置为“1”作为只重用前一个块中的树的指标。 |
|
|
4
2
更新 This page 描述了从频率表构建树的方式。作为奖励,它还通过提到一种保存树的方法来避免删除此答案:
|
|
|
5
0
更好的方法 树:
您不需要使用相同的二叉树,只需保持计算出的树深度,即编码比特数。因此,只需将未压缩值的向量[A B C D E F]按树深度排序,使用相对索引代替这个单独的向量。现在为每个深度重新创建对齐的位模式:
所有这些表都适合小的固定长度(对于长度不超过32位的霍夫曼码,LUT最多只需要31行,上面的另外两列最多可以填充32行)。
一旦你在查找表中找到一个位置(在第一列中搜索),你就可以立即从输入中获取比特数,然后获取向量的起始索引。通过在减去第一个索引后进行基本位掩码,您获得的位深度可用于直接导出调整后的索引位置。 总之:永远不要存储链接的二叉树,也不需要任何循环来执行查找,只需要在31个模式的表中的固定位置比较5个嵌套的if模式,以及一个包含解码值向量内起始偏移的31个int的表(在嵌套的if/then/else测试的第一个分支中,向量的起始偏移是隐含的,它总是零;它也是最频繁的分支,因为它与最频繁解码值的最短代码相匹配)。 |
|
AstralHex · 矩阵乘法代码工作不正常 1 年前 |
|
|
Fishie · 作为类成员的智能指针是否仍然自动释放?[关闭] 1 年前 |
|
|
Die4Toast · 递归调用成员箭头运算符-> 1 年前 |
|
|
Anka Hanım · 关于结构和动态数组地址的问题 1 年前 |