一、为什么需要哈夫曼树?
在实际开发过程中,我们常常会用到大量的条件判断,这些条件判断直接影响着程序的执行效率。比如我们在一个将分数转换成等级的程序中,很容易想到使用如下的代码来实现:
if(score < 60) {
return "不及格";
} else if(score < 70) {
return "及格";
}else if(score < 80) {
return "中等";
}else if(score < 90) {
return "良好";
} else {
return "优秀";
}
分析上面的程序的时间消耗,则可能会发现程序的缺陷。程序的时间消耗与score的分布相关,在两种极端情况下,如果score全在60以下,则每个score只需要一次比较即可;如果score全在90分以上,则每个score的结果需要经过4次比较才能返回结果,时间差异较大,因此在比较过程中我们考虑将各个score范围。按照预先设定的比例进行排序,将比例较高的放到前面,占比较小的放到后面,这样可以在一定程度上减少比较次数。假设我们已知各范围分数占比如下:
按照上面假设的比重可以发现70分以上的比重占了80%,不过按照如上的程序需要通过三次的判断才能到达,70~79分占的比重最高,占了40%,不及格所占的比例最少,我们根据比例的多少重新构建二叉树。
为了更直观的比较,我们首先对为优化之前的程序构建一棵二叉树,如下图:
按照各分数比重优化之后的二叉树,如下图:
对比两种结构,显然第二种结构的判定效率更高。那么第二种结构是不是效率最高的呢?我们该如何去设计一棵效率最优的二叉树呢? 我们把这种效率最高的二叉树称为“最优二叉树”,也称“哈夫曼树(huffman)”。
二、哈夫曼树的定义与原理
我们把上面这两棵树简化成叶子节点带权值的二叉树(节点间的边的相关数叫做权weight)。
百度百科关于权值的解释如下:
权值就是定义的路径上面的值。可以这样理解为结点间的距离。通常指字符对应的二进制编码出现的概率。
至于哈夫曼树中的权值可以理解为:权值大表明出现概率大!
一个结点的权值实际上就是这个结点子树在整个树中所占的比例。
如上图所示。A表示不及格、B表示及格、C表示中等、D表示良好、E表示优秀,其中每个分支线上的数字就是我们在前面提到的各个成绩所占的百分比。