一丶树、森林、二叉树的转换
1-1丶树转换为二叉树
1-2丶森林转换为二叉树
1-3丶二叉树转换为树
1-3丶二叉树转换为森林
二丶赫夫曼树
赫夫曼树:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。哈夫曼树(霍夫曼树)又称为最优树。
2-1丶赫夫曼树相关概念
1、路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
2、结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
2-2丶赫夫曼树的应用
1、赫夫曼编码
在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA EAR ARE ART AREA”,这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5。然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。
为使不等长编码为前缀编码(即要求一个字符的编码不能是另一个字符编码的前缀),可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,显然字使用频率越小权值越小,权值越小叶子就越靠下,于是频率小编码长,频率高编码短,这样就保证了此树的最小带权路径长度效果上就是传送报文的最短长度。因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编码的条件,又保证报文编码总长最短。
2丶赫夫曼静态编码
它对需要编码的数据进行两遍扫描:第一遍统计原数据中各字符出现的频率,利用得到的频率值创建哈夫曼树,并必须把树的信息保存起来,即把字符0-255(28=256)的频率值以2-4BYTES的长度顺序存储起来,(用4Bytes的长度存储频率值,频率值的表示范围为0--232-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。
3丶赫夫曼动态编码
动态哈夫曼编码使用一棵动态变化的哈夫曼树,对第t+1个字符的编码是根据原始数据中前t个字符得到的哈夫曼树来进行的,编码和解码使用相同的初始哈夫曼树,每处理完一个字符,编码和解码使用相同的方法修改哈夫曼树,所以没有必要为解码而保存哈夫曼树的信息。编码和解码一个字符所需的时间与该字符的编码长度成正比,所以动态哈夫曼编码可实时进行。
4、赫夫曼译码
在通信中,若将字符用哈夫曼编码形式发送出去,对方接收到编码后,将编码还原成字符的过程,称为哈夫曼译码。
5丶压缩与解压文件
任何文件都可以看作是由字节组成的字节块,将字节看作基本编码单元,一个文件就可以看作是由字节组成的信息串。对文件中各字节的出现频率进行统计,并以出现频率作为每个字节块的权值,就可以用字节为叶结点构造哈夫曼树,进而构造出各字节的对应哈夫曼编码。
对于源文件的字节编码是一种8位定长编码,而将各字节用哈夫曼编码进行重新编码,就有可能使得总的编码长度更短,这样将编码看做二进制位,每个8位作为一个字节写入压缩文件中,根据构建哈弗曼树的算法,在源文件中出现频率高的字符所对应的编码长度小,所以总的文件长度会小于原来的文件长度,从而达到文件压缩的效果。对于每一个字节块,都是8位的,所以可以用ASCII码值对应每一个字符,也就是一个0~~255的数,可以创建一个256长度的数组,来记录每一个ASCII码值出现的频率。所以需要先将要压缩的文件遍历一遍,统计一下每一个字符出现的频率,然后调用求Huffman树的函数,建立一棵对应此文件字符出现的频率的Huffman树,并且求出对应每一个ASCII码的Huffamn编码。然后再遍历一遍,每次读出一个字节,根据其ASCII码值找出对应的Huffman编码,如果Huffman编码长度不足8位时,就再从文件中读出下一个字符,并且得到他的Huffman编码,与之前的没有写入文件的编码作为一个整体;如果此时的编码长度大于或等于8了,就将它对应的ASCII码值写入压缩文件中。就这样边读源文件,边进行翻译写入压缩文件,直到读完源文件。这样就完成了文件的压缩功能,由于还学要正确的解压文件,所以在进行压缩的时候还应该把Huffman树一块保存起来,所以压缩文件的前面一部分就是压缩文件是建立的Huffman树,而压缩文件的开头四个字节用来记录文件的扩展名。
当进行解压的时候,根据压缩文件的格式,先读出源文件的扩展名,然后读出Huffman树,根据Huffman树进行解压。解压时,同样可以一边解压一边写入解压后的文件中,即每读出一个字节,就根据他的ASCII码值,查找Huffman树对应的叶子节点,叶子节点的下标就是源文件对应的ASCII码值,然后将得到的ASCII码值写入压缩后的文件中,就这样直到读完解压文件,就完成了文件的解压工作。