美文网首页
数据结构(十四) -- Huffman树

数据结构(十四) -- Huffman树

作者: 峰峰小 | 来源:发表于2016-11-01 20:56 被阅读180次

    本文将通过 Huffman 编码树的构造问题,介绍优先队列结构的具体应用。

    二进制编码

    通讯系统可以帮助人们将一段信息从发送端传送给接收端。最常见的信息形式是字符串,即由来自某一有限字符集Σ 的若干个字符组成的一个序列M = (x1, …, xn),xi∈Σ,1≤i≤n。在将M 加载至信道上并发送之前,首先需要对M 进行编码(Encoding)。通常采用的都是二进制编码,以英文字母集Σ = {A, B, C, …, Z}为例,若需要传送字符串“MAIN”,一种可行的编码方式就是:

    e('N') = "00"
    e('A') = "010"
    e('I') = "011"
    e('M') = "1"

    若令每个字符分别对应于一匹叶子,则从根节点通往每匹叶子的路径,就对应于相应字符的二进制编码,这样的一棵树也称作二叉编码树。

    二进制解码

    反过来,根据这棵编码树也可以便捷地完成解码工作。

    • 从头开始扫描接收到的二进制信息流;

    • 从根节点开始,根据各比特位不断进入下一层节点;

    • 到达叶子节点后,输出其对应的字符;

    • 然后重新回到根节点,并继续扫描二进制流。

    实际上,这一过程可以在接收过程中实时进行,而不必等到所有的比特位都到达之后才开始解码。

    最优编码树

    在实际的通讯系统中,信道的使用效率是个很重要的问题,这在很大程度上取决于编码算法本身的效率。很自然地,我们当然希望能够用尽可能少的比特位来表示字符串。那么,如何做到这一点呢?在什么情况下能够做到这一点呢?

    先介绍一项编码效率的重要指标——平均编码长度

    在二叉编码树中每个字符 c 的编码长度为对应的叶子的深度 depth(c)。

    Σ 中各字符的编码长度总和除以字符集 Σ 就是单个字符的平均编码长度。

    对于任一字符集Σ,若在所有的编码方式中,某一编码方式 e() 使得平均编码长度最短,则称 e() 为 Σ 的一种最优编码,与之对应的编码树称作 Σ 的一棵最优编码树。

    我们注意到,对于同一字符集Σ,所有深度不超过|Σ|的编码树只有有限棵,因此其中的总编码长度最小者必然存在(尽管不见得唯一)。

    如何找到最优编码树

    首先需要更加深入地了解最优编码树的性质,在最优二叉编码树中:

    • (1) 每个内部节点的度数均为 2

    • (2) 各叶子之间的深度差不超过 1

    推论:基于由 2 | Σ | -1 个节点构成的完全二叉树 T,将 Σ 中的字符任意分配给 T 的 | Σ | 匹叶子,即可得到 Σ 的一棵最优编码树。

    这一推论也直接给出了一个构造最优编码树的算法。

    带权编码

    上面所介绍的最优编码树,在实际应用中的利用价值并不大。不难看出,只有当 Σ 中各个字符在信息串中出现的概率相等时,其最优性才有意义,遗憾的是,这一条件很难满足。

    在实际应用中,Σ 中各字符的出现频率不仅很少相等或相近,而且往往相差悬殊。以英文信息串为例,'e'、't'等字符的出现频率通常都是'z'、'j'等字符的数百倍。在这种情况下,就应该从另一角度衡量每个字符的编码长度。

    若假设字符 c 出现的概率为 p(c) ≥ 0, 其在二叉编码树中对应的叶子的深度记作depth(c),则:

    • 每个字符 c∈Σ 的带权编码长度为|e(c)| = depth(c) × p(c)
    • 对于任一字符集 Σ 的任一编码方式 e(),Σ 中各字符的平均带权编码长度总和|e(c)|称作 e()(或其对应的二叉编码树)的平均带权编码长度。

    例如:信息串" M A M A N I "

    各字符出现的概率分别为p('M') = 2/6、p('A') = 2/6、p('I') = 1/6 和p('N') = 1/6

    则各字符的带权编码长度分别为:
    |e('M')| = 3×(2/6) = 1
    |e('A')| = 3×(2/6) = 1
    |e('I')| = 2×(1/6) = 1/3
    |e('N')| = 1×(1/6) = 1/6

    相应地,这一编码方式对应的平均带权编码长度就是:
    |e('M')| + |e('A')| + |e('I')| + |e('N')| = 2.5

    如何找到最优带权编码树(不唯一)

    首先

    ** 完全二叉编码树 ≠ 平均带权编码最短 **

    最优带权编码树有以下性质:

    • 在最优带权编码树中,内部节点的度数均为 2

    • 对于字符出现概率为 p()的任一字符集 Σ ,若字符 x 和 y 在所有字符中的出现概率最低,则必然存在某棵最优带权编码树,使得 x 和 y 在其中同处于最底层,而且互为兄弟。

    对于字符出现概率满足一定分布的任一字符集 Σ ,我们都可以按照如下算法来构造其对应的 Huffman 编码树:

    首先,对应于 Σ 中的每一字符,分别建立一棵由单个节点组成树,其权重就是该字符出现的频率,这|Σ |棵树组成一个森林 F。

    接下来,从 F 中选出权重最小的两棵树,创建一个新节点,并分别以这两棵树作为其左、右子树,从而将它们合成为一棵更高的树,其权重等于其左、右子树权重之和。

    这一选取、合并的过程将反复进行,直到最后 F 中只剩下一棵树⎯⎯它就是我们所需要的 Huffman 编码树。

    举个例子:

    字符 出现频率
    A 623
    B 99
    C 239
    D 290
    E 906
    F 224
    基于优先队列的 Huffman 树构造算法

    具体方法是:

    • 1,始终将森林中的所有树(根)组织为一个优先队列,比如基于二叉堆实现的优先队列。

    • 2,这样,只要连续地调用 delMin()方法两次,就可以找出当前权重最小的两棵树。

    • 3,在将这两棵树合并为一棵新树之后,可以调用 insert()方法将其重新插入优先队列。

    • 4,这一过程将反复进行,每迭代一次,森林的规模就会减小 1。

    因此,经过 n-1 次迭代,森林中将只包含一棵树,即 Huffman 编码树。

    相关文章

      网友评论

          本文标题:数据结构(十四) -- Huffman树

          本文链接:https://www.haomeiwen.com/subject/cqveuttx.html