前面章节分别学习了树、森林与二叉树的转换、线索二叉树、树、二叉树的基本知识。本节接着学习"哈夫曼树"
在复杂的if...else或switch...case语句中,判断的次序影响程序执行时间。之前学习过,凡是在某个点有两种互斥结果的均可以使用二叉树来表示。而最优的表示法即哈夫曼表示法。原则上,它通过将最大量置顶构造出一颗"最优树",即带权路径长度最短
基本概念
路径:节点之间的连线
节点的路径长度:节点之间连线的个数(路径的个数)
树的路径长度:树根到每个节点的路径和,相同节点数的二叉树,完全二叉树的路径长度最短
权:节点的标识,一般为可计算的数学标识
节点的带权路径长度:根节点到该节点之间的路径长度与目标节点权值的乘积
树的带权路径长度:树中所有叶子节点的带权路径长度
n个叶子节点的哈夫曼树有2n-1个节点,需经过n-1次合并,将产生n-1个新节点,每个节点的度都不为
1。所构建的哈夫曼树的层级为n-1
构造哈夫曼树
构造过程(哈夫曼树中权值越大离根越近)
构造森林,每一颗树都是以权值为根构造的单点树
构造一颗新二叉树,其左右子树根为最小权值树,该二叉树的根的权值为左右子树根权值的和, 并从森林中删除对应的树
将二叉树作为森林的成员
图示如下
假设有如下一组值,它们代表节点的权值
[1,3,4,5,8]
构造森林如下,该森林由一颗颗只有一个根节点的树组成
构建一颗二叉树,其左右子树根从森林中挑选最小的两个,该颗二叉树的根节点的权值=挑选的权
值和
从森林中删除挑选出的两颗树,并将构造的新二叉树入森林,此时森林如下
继续挑选最小的两颗树,并据此构建一颗二叉树,即
继续将新二叉树入队森林,并剔除挑选的树,此时森林如下
重复以上步骤,即挑选5和8组成新二叉树,即
此时森林如下
继续以上两部,则最终生成的树如下
JavaScript实现如下
由于n个叶子节点的哈夫曼树有2n-1个节点,故对于数组[1,3,4,5,8]的数组大小为9
节点结构如下
代码实现如下
为了方便挑选最小值,先对数组进行一次排序,即
构建一个具有2n-1个成员的数组,并初始化
每次从数组中挑选最小的两颗树并构建一颗二叉树,根的权重为挑选的权值和,挑选的树分别
视为二叉树的左右子树根
生成的哈夫曼树如下
完整实现算法如下
哈夫曼编码
数据的传输是先将数据转换成二进制0和1之后传到目的地后解码,数据量越小传输效率越大。在同样的待传数据中,如何做到最小,正是哈夫曼编码解决的问题
哈夫曼编码的理论基础为前缀编码,由于叶子节点的特性保证了其不可能为另一叶子节点的双亲,故其任一字符的编码均不是令一字符编码的前缀
哈夫曼编码的规则如下
对数据进行概率分析,得到某个字符出现的次数
若有串"aabca",则a的概率为3,b为1,c为1
将概率作为权值构造哈夫曼树,在构建过程中生成的二叉树将其左路径标记为0右为1
从哈夫曼树的根节点到叶子节点的路径标记作为字符编码,则
a:0 b:10 c:11
JavaScript实现如下
假设有字符串:'abcaabac',则计算的每个字符出现的频率如下
得到的哈夫曼树如下
从叶子节点向上查找,按照左0右1标记路径直到哈夫曼树的根,则为该叶子节点的哈夫曼编码。算
法实现如下
结果如下