先决条件:霍夫曼编码,霍夫曼解码
自适应霍夫曼编码也称为动态霍夫曼编码。该实现是使用Vitter算法完成的。
编码方式
包含字母的字符串的自适应霍夫曼编码:
令m为字母总数。所以m = 26
对于Vitter Algorithm,找到参数e&r使得
m = 2e + r and 0 ≤ r ≤ 2e
Therefore, for m = 26 we get e = 4 & r = 10
有两种类型的代码:NYT代码和固定代码。
NYT code = Traversing tree from the root node to that particular NYT node.
对于固定代码,可以从以下两个条件计算得出:
- 如果0≤k≤2r,则字母Sk被编码为(e + 1)位中(k-1)的二进制表示。 (其中k是按字母顺序排列的字母的位置)
- 否则,将字母Sk编码为e位的(kr-1)的二进制表示形式。
更新树木
Vitter算法中的树更新遵循隐式编号。在隐式编号中
- 节点以递增顺序编号,即按级别和从左到右的顺序
- 具有相同权重和类型的节点一起形成一个块
- 通过增加权重的顺序,块彼此相关
- 内部节点由椭圆形表示。内部节点的权重=子节点权重的总和
- 外部节点由正方形表示。外部节点的权重=最初为1,如果重复,则将权重增加1
更新树的步骤:
- 使用NYT节点初始化树
- 对于首次识别的符号,将初始NYT节点进一步划分为NYT节点,新节点初始化为该符号,权重= 1。
- 将子节点权重之和分配给父节点
- 如果遇到重复的符号,则权重将更新为该符号。
注意:在“树中更新”期间,如果左侧子树的权重大于右侧子树的权重,则必须交换节点。
例子
code = "aardvark"
The final Code we get is:
00000 1 010001 0000011 0001011 0 10 110001010
a a r d v a r k
解释:
对于字符串代码=“ aardvark”,e = 5,r = 10
如上图所示,使用权重为0的NYT节点初始化树。
- 对于符号’a’,k = 1。
NYT Code = "" (initially tree is empty)
对于固定代码:由于k <2r,即1 <2 * 10,满足条件(1)
因此固定代码是(k-1)= 0的二进制表示形式为5位表示形式Fixed Code = "00000"
Huffman Code for symbol for 'a' is "00000"
- 对于树中已经存在的符号“ a”。遍历树到符号“ a”,我们得到的代码为“ 1”
Huffman Code for symbol for 'a' is "1"
- 对于符号’r’,k = 18。
NYT Code = "0" (traversing up to NYT Node)
对于固定代码:当k> 2r,即18> 2 * 10时,满足条件(2)
因此固定代码是(k-1 = 17)的二进制表示形式为5位表示形式Fixed Code = "10001"
Huffman Code for symbol for 'r' is "010001"
- 对于符号“d”,k = 4。
NYT Code = "000" (traversing up to NYT Node)
对于固定代码:由于k <2r,即4 <2 * 10,满足条件(1)
因此固定代码是(k-1 = 3)的二进制表示形式为5位表示形式Fixed Code = "00011"
Huffman Code = "00000011"
- 对于符号’v’,k = 22。
NYT Code = "000" (traversing up to NYT Node)
对于固定代码:当k> 2r,即22> 2 * 10时,满足条件(2)
因此固定代码是(kr-1 = 11)的二进制表示形式为4位表示形式Fixed Code = "1011"
Huffman Code = "0001011"
- 交换左子树的节点和右树,因为它违反了属性
- 对于树中已经存在的符号“ a”。遍历树直到符号“ a”,我们得到的代码为“ 0”
Huffman Code for symbol for 'a' is "0"
- 对于树中已经存在的符号“ r”。遍历树直到符号“ a”,我们得到的代码为“ 10”
Huffman Code for symbol for 'r' is "10"
- 对于符号’k’,k = 11。
NYT Code = "1100" (traversing up to NYT Node)
对于固定代码:由于k <2r,即11 <2 * 10,满足条件(1)
因此固定代码是(k-1 = 10)的二进制表示形式为5位表示形式Fixed Code = "01010"
Huffman Code for symbol for 'r' is "110001010"
解码
解码步骤:
- 读取二进制字符串
- 如果遇到的叶节点是NYT
- 读取下一个e位
- 如果e位值
- 如果e位的值> r,则为了获得所需的符号,将e位转换为e位的十进制值+ r + 1
- 如果e位值
- 读取下一个e位
例子:
code = "00000101000100000110001011010110001010"
We get final decoded code as
00000 1 0 10001 00 00011 000 1011 0 10 1100 01010
a a NYT r NYT d NYT v a r NYT k
解释:
- 通过读取第一个e位开始解码。因此,前4位为0000,转换为十进制= 0。
现在值0现在根据条件(1),将第一个e + 1 = 5位转换为十进制并加1。 00000 = 0 0 + 1 = 1, which is value for alphabet a.
更新树并在树中添加符号“ a”的节点
- 读取给定代码中的下一位并遍历树。我们到达外部叶子节点“ a”。因此,下一个已解码符号是“ a”。
- 读取给定代码的下一组位并遍历树。我们将0作为NYT节点。到达NYT节点后,读取e位,它们是1000。将1000转换为十进制是8。由于8
现在,将e + 1位转换为十进制并加1。 10001 = 17 17 + 1 = 18, which is value for alphabet r.
更新树,并在树中为符号“ r”添加一个节点。
- 读取下一组位并遍历树,我们到达00处的NYT节点。读取e位为0001。将0001转换为十进制为1。由于1
现在,将e + 1位转换为十进制并加1。 00011 = 3 3 + 1 = 4, which is value for alphabet d.
更新树并在树中为符号“ d”添加一个节点。
- 读取下一组位并遍历Tree,我们到达000处的NYT节点。读取e位为1011。将1011转换为十进制为11。当11> r满足条件(2)时。
现在,以十进制转换k + r + 1位并解码该符号。10110 = 22, which is value for alphabet v.
更新树,并在树中为符号“ v”添加一个节点。
- 读取下一组位并遍历树,我们在0处得到符号’a’。更新树并在树中添加符号’a’的节点。
- 读取下一组位并遍历树,我们在10获得符号“ r”。更新树并在树中添加符号“ a”的节点。
- 读取下一组位并遍历树,我们在1100到达NYT节点。读取e位0101。将0101转换为十进制为9。9
现在,将e + 1位转换为十进制并加1。 01000 = 8, 8 + 1 = 9. which is value for alphabet k.
更新树,并在树中为符号“ v”添加一个节点。