赫夫曼树的应用
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,自然会想到设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。
为使不等长编码为前缀编码(即要求一个字符的编码不能是另一个字符编码的前缀),可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,求出此树的最小带权路径长度就等于求出了传送报文的最短长度。因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编码的条件,又保证报文编码总长最短。
哈夫曼静态编码:它对需要编码的数据进行两遍扫描:第一遍统计原数据中各字符出现的频率,利用得到的频率值创建哈夫曼树,并必须把树的信息保存起来,即把字符0-255(2^8=256)的频率值以2-4BYTES的长度顺序存储起来,(用4Bytes的长度存储频率值,频率值的表示范围为0–2^32-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。
哈夫曼动态编码:动态哈夫曼编码使用一棵动态变化的哈夫曼树,对第t+1个字符的编码是根据原始数据中前t个字符得到的哈夫曼树来进行的,编码和解码使用相同的初始哈夫曼树,每处理完一个字符,编码和解码使用相同的方法修改哈夫曼树,所以没有必要为解码而保存哈夫曼树的信息。编码和解码一个字符所需的时间与该字符的编码长度成正比,所以动态哈夫曼编码可实时进行。[3]
2、哈夫曼译码
在通信中,若将字符用哈夫曼编码形式发送出去,对方接收到编码后,将编码还原成字符的过程,称为哈夫曼译码。
三、对栈的应用(计算四则运算表达式)以及赫夫曼树的应用(赫夫曼编码)部分的体会
表达式求值:因为读的时候是一个字符一个字符读的,如果12345*456789不能分开读,用字符串截取有效部分,即查找当前运算符到下一个运算符的位置,截取中间的数字存在字符串数组里面,然后扫描字符串数组,将里面的数字和字母分到两个栈里面,扫描表达式时,遇到运算符的操作有多种情况,需要根据运算符优先级来进行选择,所以需要编写一个判断优先级的函数,也需要将运算符优先级存起来。
赫夫曼编码:
哈夫曼编码步骤:
一、对给定的n个权值{W1,W2,W3,…,Wi,…,Wn}构成n棵二叉树的初始集合F= {T1,T2,T3,…,Ti,…,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。(为方便在计算机上实现算 法,一般还要求以Ti的权值Wi的升序排列。)
二、在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
三、从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。
四、重复二和三两步,直到集合F中只有一棵二叉树为止。
简易的理解就是,假如我有A,B,C,D,E五个字符,出现的频率(即权值)分别为5,4,3,2,1,那么我们第一步先取两个最小权值作为左右子树构造一个新树,即取1,2构成新树,其结点为1+2=3,如图:
虚线为新生成的结点,第二步再把新生成的权值为3的结点放到剩下的集合中,所以集合变成{5,4,3,3},再根据第二步,取最小的两个权值构成新树,如图:
再依次建立哈夫曼树,如下图:
其中各个权值替换对应的字符即为下图:
所以各字符对应的编码为:A->11,B->10,C->00,D->011,E->010
霍夫曼编码是一种无前缀编码。解码时不会混淆。其主要应用在数据压缩,加密解密等场合。
这次写哈夫曼编码主要是为了锻炼算法,MFC并没有花太多时间,在优化算法上,采用红黑树统计各个字母出现的次数,这样只需要logn就可以实现,然后更具权值建立霍夫曼树,建立哈夫曼树的过程中采用优先级队列,这样的话每次取出两个最小的权值只需要logn的时间,总共取n-1次时间复杂度是nlogn这想对与直接暴力的o(n*n)在时间上大大优化,当文件比较大的时候效率压缩时间明显得到提高。然后将得到的01串,用C++STL里面的map存储,定义map然后每次加8个01串,压缩成一个字符,用STL里面的bitset<8>压缩成一个字符,最后如果最后一个不满8个,那就用0补齐,并且将有效的位数存在文件最后一位,当读取的时候先用文件定位到最后一个字符,将有效的字符个数取出来,然后定位到倒数第二个,设立文件指针,然后再定位到第一个字符,开始读,知道读到倒数第二个,最后一个单独读,读到有效位置为止,然后通过密码本建立的哈夫曼树解码
部分算法代码:
简单的用MFC进行可视化压缩
通过这次哈夫曼编码译码器的制作,对C++的STL又进一步了解了比如优先级队列,bitsit类等等,也初探红黑树的强大,将ACM学到的算法在此得到了优化,还学习了C++文件的定位,文件指针等等,遗憾的是没有太多时间学习MFC,就粗糙的用VS2013写了下可视化编程
版权声明:本文为博主原创文章,未经博主允许不得转载。