文本文件是由不同类型的字符组合而成的,而且不同字符出现的次数也是不一样的。例如,在某个文本文件中,A出现了100次左右,Q仅仅用到了3次,类似这样的情况很常见。哈夫曼算法的关键就在于多次出现的数据用小于8位的字节数表示,不常用的数据则可以使用超过8位的字节数表示。A和Q都用8位来表示时,原文件的大小就是100次* 8位+3次* 8位= 824位,假设A用2位,Q用10位来表示就是2* 100+3* 10= 230位。

哈夫曼算法比较复杂,在深入了解之前我们先了解一下莫尔斯编码,你一定看过美剧或者战争片的电影,在战争中的通信经常采用莫尔斯编码来传递信息,例如下面:
哈夫曼算法和莫尔斯编码
下面是莫尔斯编码的示例,把1看做是短点,把11看作长点即可。
哈夫曼算法和莫尔斯编码
莫尔斯编码一般把文本中出现最高频率的字符用短编码来表示。如表所示,假如表示短点的位是1,表示长点的位是11的话,那么E (嘀)这一数据的字符就可以用1来表示,C (滴答滴答)就可以用9位的110101101 来表示。在实际的莫尔斯编码中,如果短点的长度是1,长点的长度就是3,短点和长点的间隔就是1。这里的长度指的就是声音的长度。比如我们想用上面的AAAAAABBCDDEEEEEF例子来用莫尔斯编码重写,在莫尔斯曼编码中,各个字符之间需要加入表示时间间隔的符号。这里我们用00加以区分。

所以,AAAAAABBCDEEEEEF这个文本就变为了A* 6次+B* 2次+C* 1次+D* 2次+E* 5次+F* 1次+字符间隔* 16=4位* 6次+8位* 2次+9位* 1次+ 6位* 2次+ 1位* 5次+8位* 1次+2位* 16次= 106位= 14字节。所以使用莫尔斯电码的压缩比为141/17 = 82%。效率并不突出。

用二叉树实现哈夫曼算法
莫尔斯编码是根据日常文本中各字符的出现频率来决定表示各字符的编码数据长度的。不过,在该编码体系中,对AAAAAABBCDDEEEEEF这文本来说并不是效率最高的。我们来看一下哈夫曼算法。哈夫曼算法是指,为各压缩对象文件分别构造最佳的编码体系,并以该编码体系为基础来进行压缩。因此,用什么样的编码(哈夫曼编码)对数据进行分割,就要由各个文件而定。用哈夫曼算法压缩过的文件中,存储着哈夫曼编码信息和压缩过的数据。
哈夫曼算法和莫尔斯编码
我们在对AAAAAABBCDDEEEEEF中的A-F这些字符,按照出现频率高的字符用尽量少的位数编码来表示这一原则进行整理。按照出现频率从高到低的顺序整理后,结果如下:
哈夫曼算法和莫尔斯编码
在上表的编码方案中,随着出现频率的降低,字符编码信息的数据位数也在逐渐增加,从最开始的1位、2位依次增加到3位。不过这个编码体系是存在问题的,你不知道100这个3位的编码,它的意思是用1、0、0这三个编码来表示E、A、A呢?还是用10、0来表示B、A呢?还是用100来表示C呢。

而在哈夫曼算法中,通过借助哈夫曼树的构造编码体系,即使在不使用字符区分符号的情况下,也可以构建能够明确进行区分的编码体系。不过哈夫曼树的算法要比较复杂,下面是一个哈夫曼树的构造过程:
哈夫曼算法和莫尔斯编码
哈夫曼树能够提升压缩比率
使用哈夫曼树之后,出现频率越高的数据所占用的位数越少,这也是哈夫曼树的核心思想。通过上图的步骤二可以看出,枝条连接数据时,我们是从出现频率较低的数据开始的。这就意味着出现频率低的数据到达根部的枝条也越多。而枝条越多则意味着编码的位数随之增加。

接下来我们来看一下哈夫曼树的压缩比率,用上图得到的数据表示AAAAAABBCDDEEEEEF为000000000000 100100 110 101101 0101010101 111,40位=5字节。压缩前的数据是17字节,缩后的数据竟然达到了惊人的5字节,也就是压缩比率= 5/ 17 = 29%如此高的压缩率,简直是太惊艳了。

无论哪种类型的数据,都可以用哈夫曼树作为压缩算法:
哈夫曼算法和莫尔斯编码

相关文章: