随着网络与多媒体技术的兴起,人们需要存储和传输的数据越来越多,数据量越来越大,以前带宽有限的传输网络和容量有限的存储介质难以满足用户的需求。
特别是声音、图像和视频等媒体在人们的日常生活和工作中的地位日益突出,这个问题越发显得严重和迫切。如今,数据压缩技术早已是多媒体领域中的关键技术之一。
一、什么是哈弗曼压缩
Huffman(哈夫曼)算法在上世纪五十年代初提出来了,它是一种无损压缩方法,在压缩过程中不会丢失信息熵,而且可以证明Huffman算法在无损压缩算法中是最优的。Huffman原理简单,实现起来也不困难,在现在的主流压缩软件得到了广泛的应用。对应用程序、重要资料等绝对不允许信息丢失的压缩场合,Huffman算法是非常好的选择。
二、怎么实现哈弗曼压缩
哈夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。哈夫曼压缩属于可变代码长度算法一族。意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。
故我们得了解几个概念:
1、二叉树:在计算机科学中,二叉树是每个结点最多有两个子树的有序树。通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree)。
2、哈夫曼编码(Huffman Coding):是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。uffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长 度最短的码字,有时称之为最佳编码,一般就叫作Huffman编码。
三、哈夫曼编码生成步骤:
①扫描要压缩的文件,对字符出现的频率进行计算。
②把字符按出现的频率进行排序,组成一个队列。
③把出现频率最低(权值)的两个字符作为叶子节点,它们的权值之和为根节点组成一棵树。
④把上面叶子节点的两个字符从队列中移除,并把它们组成的根节点加入到队列。
⑤把队列重新进行排序。重复步骤③④⑤直到队列中只有一个节点为止。
⑥把这棵树上的根节点定义为0(可自行定义0或1)左边为0,右边为1。这样就可以得到每个叶子节点的哈夫曼编码了。
既如 (a)、(b)、(c)、(d)几个图,就可以将离散型的数据转化为树型的了。
如果假设树的左边用0表示右边用1表示,则每一个数可以用一个01串表示出来。
则可以得到对应的编码如下:
1-->110
2-->111
3-->10
4-->0
每一个01串,既为每一个数字的哈弗曼编码。
为什么能压缩:
压缩的时候当我们遇到了文本中的1、2、3、4几个字符的时候,我们不用原来的存储,而是转化为用它们的01串来存储不久是能减小了空间占用了吗。(什么01串不是比原来的字符还多了吗?怎么减少?)大家应该知道的,计算机中我们存储一个int型数据的时候一般式占用了2^32-1个01位,因为计算机中所有的数据都是最后转化为二进制位去存储的。所以,想想我们的编码不就是只含有0和1嘛,因此我们就直接将编码按照计算机的存储规则用位的方法写入进去就能实现压缩了。
比如:
1这个数字,用整数写进计算机硬盘去存储,占用了2^32-1个二进制位
而如果用它的哈弗曼编码去存储,只有110三个二进制位。
效果显而易见。哈夫曼编码(Huffman Coding)是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。 Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长 度最短的码字,有时称之为最佳编码,一般就叫作Huffman编码。 以哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。 在计算机信息处理中,“哈夫曼编码”是一种一致性编码法(又称"熵编码法"),用于数据的无损耗压缩。这一术语是指使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。这张编码表的特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均期望长度降低,从而达到无损压缩数据的目的)。这种方法是由DavidAHuffman发展起来的。 例如,在英文中,e的出现概率很高,而z的出现概率则最低。当利用哈夫曼编码对一篇英文进行压缩时,e极有可能用一个位(bit)来表示,而z则可能花去25个位(不是26)。用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个位。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。WinRAR是采用它自己的独创的压缩算法。
希望你能看看最优二叉树(哈夫曼树),理解哈夫曼编码的原理,对你的这个压缩算法会有很明晰的指导和解惑作用WinRAR是采用它自己的独创的压缩算法。
压缩处理都是以二进制的方式进行的。这和你的编码有关。只要是处理后的结果比原文档文件小,而且是可逆的还原,就是无压缩。
压缩率的大小和你的编码方式有关。
无损压缩是指重构压缩数据(还原,解压缩),而重构数据与原来数据完全相同。该方法用于那些要求重构信号与原始信号完全一致的场合,如文本数据、程序和特殊应用场合的图像数据(如指纹图像、医学图像等)的压缩。这类算法压缩率较低,一般为1/2~1/5。典型的无损压缩算法有:Shanno-Fano编码、Huffman(哈夫曼)编码、算术编码、游程编码、LZW编码等。
基于哈夫曼编码原理的压缩算法:
哈夫曼算法的过程为:统计原始数据中各字符出现的频率;所有字符按频率降序排列;
比如有一个字符串:aaaaaaaaaabbbbbbcccd
原文件大小存储需要20个字节。如果按频率出现的次数高低,给予字符串中的每个字符不同的编码长度,就可以达到压缩的目的。
如
a编码为01(占用2个bit)
b编码为00(占用2个bit)
c编码为000,(占用3个bit)
c编码为001,(占用3个bit)
那就压缩后的总长为(210+26+33+13)/8 =55个字节。
另外在解码的时候,要告之对方你的编码方式,需要把编码的规则传递过去。
如果对于以上字符串,你也可以按aaaaaaaaaa编码成一个1,bbbbbb为2,ccc为3,d为4。这样压缩后的内容为最小,但是要注意一点,这时你的编码规则为最大,你要把你的编码规则发给对方的时候,有可能编编解码规则文件可能会比压缩后的内容还要大。最终结果为造成压缩后的文件比原文件还要大。哈夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。哈夫曼压缩属于可变代码长度算法一族。意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。有人用C函数写了这个编码,见下面链接
>怎么没人回答呢 我来回答吧 我想从压缩文件的原理能得到你这个问题的答案(有点长,请耐心看,绝对长知识): 压缩文件的运行原理 如果您从互联网上下载了许多程序和文件,可能会遇到很多ZIP文件。这种压缩机制是一种很方便的发明,尤其是对网络用户,因为它可以减小文件中的比特和字节总数,使文件能够通过较慢的互联网连接实现更快传输,此外还可以减少文件的磁盘占用空间。在下载了文件后,计算机可使用WinZip或Stuffit这样的程序来展开文件,将其复原到原始大小。如果一切正常,展开的文件与压缩前的原始文件将完全相同。
乍一听好像很神秘:您是怎样减少比特和字节的数量并将它们原封不动地还原回去的呢?等一切水落石出之后,您会发现这个过程背后的基本理念其实非常简单明了。在本文中,我们将讨论这种通过简单压缩来明显减小文件的方法。
大多数计算机文件类型都包含相当多的冗余内容——它们会反复列出一些相同的信息。文件压缩程序就是要消除这种冗余现象。与反复列出某一块信息不同,文件压缩程序只列出该信息一次,然后当它在原始程序中出现时再重新引用它。
以我们熟悉的信息类型——单词——为例子。
肯尼迪(John F Kennedy)在1961年的就职演说中曾说过下面这段著名的话:
Ask not what your country can do for you——ask what you can do for your country(不要问国家能为你做些什么,而应该问自己能为国家做些什么。)
这段话有17个单词,包含61个字母、16个空格、1个破折号和1个句点。如果每个字母、空格或标点都占用1个内存单元,那么文件的总大小为79个单元。为了减小文件的大小,我们需要找出冗余的部分。
我们立刻发现:
如果忽略大小写字母间的区别,这个句子几乎有一半是冗余的。九个单词(ask、not、what、your、country、can、do、for、you)几乎提供了组成整句话所需的所有东西。为了构造出另一半句子,我们只需要拿出前半段句子中的单词,然后加上空格和标点就行了。
大多数压缩程序使用基于自适应字典的LZ算法来缩小文件。“LZ”指的是此算法的发明者Lempel和Ziv,“字典”指的是对数据块进行归类的方法。
排列字典的机制有很多种,它也可以像编号列表那样简单。在我们检查肯尼迪这句著名讲话时,可以挑出重复的单词,并将它们放到编号索引中。然后,我们直接写入编号而不是写入整个单词。
因此,如果我们的字典是:
ask
what
your
country
can
do
for
you
我们的句子现在就应该是这样的:
1 not 2 3 4 5 6 7 8-- 1 2 8 5 6 7 3 4
如果您了解这种机制,那么只需使用该字典和编号模式即可轻松重新构造出原始句子。这就是在展开某个下载文件时,计算机中的解压缩程序所做的工作。你可能还遇到过能够自行解压缩的压缩文件。若要创建这种文件,编程人员需要在被压缩的文件中设置一个简单的解压缩程序。在下载完毕后,它可以自动重新构造出原始文件。
但是使用这种机制究竟能够节省多少空间呢?“1 not 2 3 4 5 6 7 8——1 2 8 5 6 7 3 4”当然短于“Ask not what your country can do for you-- ask what you can do for your country”,但应注意的是,我们需要随文件一起保存这个字典。
在实际压缩方案中,计算出各种文件需求是一个相当复杂的过程。让我们回过头考虑一下上面的例子。每个字符和空格都占用1个内存单元,整个原句要占用79个单元。压缩后的句子(包括空格)占用了37个单元,而字典(单词和编号)也占用了37个单元。也就是说,文件的大小为74个单元,因此我们并没有把文件大小减少很多。
但这只是一个句子的情况!可以想象的是,如果用该压缩程序处理完肯尼迪讲话的其余部分,我们会发现这些单词以及其他单词重复了更多次。而且,正如下一节所言,为了得到尽可能高的组织效率,可以对字典进行重写。
在上一个的例子中,我们挑出了所有重复的单词并将它们放在一个字典中。对于我们来说,这是最显而易见的字典编写方法。但是压缩程序却不这样认为:它对单词没有概念——它只会寻找各个模式。为了尽可能减小文件的大小,它会仔细挑选出最优模式。
如果从这个角度处理该句子,我们最终会得到一个完全不同的字典。
如果压缩程序扫描肯尼迪的这句话,它遇到的第一个冗余部分只有几个字母长。在ask not what your中,出现了一个重复的模式,即字母t后面跟一个空格——在not和what中。如果压缩程序将此模式写入字典,则每次出现“t”后面跟一个空格的情况时,它会写入一个“1”。但是在这个短句中,此模式的出现次数不够多,不足以将其保留为字典中的一个条目,因此程序最终会覆盖它。
程序接下来注意到的内容是ou,在your和country中都出现了它。如果这是一篇较长的文档,将此模式写入字典会节省大量空间——在英语中ou是一个十分常见的字母组合。但是在压缩程序看完整个句子后,它立即发现了一个更好的字典条目选择:不仅ou发生了重复,而且your和country整个单词都发生了重复,并且它们实际上是作为一个短语your country一起发生重复的。在本例中,程序会用your country条目覆盖掉字典中的ou条目。
短语can do for也发生了重复,一次后面跟着your,另一次跟着you,因此我们又发现can do for you也是一种重复模式。这样,我们可以用一个数字来代替15个字符(包含空格),而your country只允许我们用一个数字代替13个字符(包含空格),所以程序会用r country条目覆盖your country条目,然后再写入一个单独的can do for you条目。程序通过这种方式继续工作,挑出所有重复的信息,然后计算应该将哪一种模式写入字典。基于自适应字典的LZ算法中的“自适应”部分指的就是这种重写字典的能力。程序执行此工作的过程实际上非常复杂。
无论使用什么方法,这种深入搜索机制都能比仅仅挑出单词这种方法更有效率地对文件进行压缩。如果使用我们上面提取出的模式,然后用“__”代替空格,最终将得到下面这个更大的字典:
ask__
what__
you
r__country
__can__do__for__you
而句子则较短:
“1not__2345__--__12354”
句子现在占用18个内存单元,字典占用41个单元。所以,我们将文件总大小从79个单元压缩到了59个单元!这仅仅是压缩句子的一种方法,而且不一定是最高效的方法。(看看您能找到更好的方法吗!)
那么这种机制到底有多好呢?文件压缩率取决于多种因素,包括文件类型、文件大小和压缩方案。
在世界上的大多数语言中,某些字母和单词经常以相同的模式一起出现。正是由于这种高冗余性,而导致文本文件的压缩率会很高。通常大小合适的文本文件的压缩率可以达到50%或更高。大多数编程语言的冗余度也很高,因为它们的命令相对较少,并且命令经常采用一种设定的模式。对于包含大量不重复信息的文件(例如图像或MP3文件),则不能使用这种机制来获得很高的压缩率,因为它们不包含重复多次的模式。
如果文件有大量重复模式,那么压缩率通常会随着文件大小的增加而增加。从我们的例子中就可以看出这一点——如果我们摘录的肯尼迪讲话再长一些,您会发现又多次出现了我们字典中的模式,因此能够通过每个字典条目节省更多的文件空间。此外,对于更大的文件,还可能出现具有更大普遍性的模式,从而能够创建出效率更高的字典。
此外,文件压缩效率还取决于压缩程序使用的具体算法。有些程序能够在某些类型的文件中更好地寻找到模式,因此能更有效地压缩这些类型的文件。其他一些压缩程序在字典中又使用了字典,这使它们在压缩大文件时表现很好,但是在压缩较小的文件时效率不高。尽管这一类的所有压缩程序都基于同一个基本理念,但是它们的执行方式却各不相同。程序开发人员始终在尝试建立更好的压缩机制。
有损压缩和无损压缩
我们在上文中讨论的压缩类型称为无损压缩,因为您重新创建的文件与原始文件完全相同。所有无损压缩都基于这样一种理念:将文件变为“较小”的形式以利于传输或存储,并在另一方收到它后复原以便重新使用它。
有损压缩则与此大不相同。这些程序直接去除“不必要”的信息,对文件进行剪裁以使它变得更小。这种类型的压缩大量应用于减小位图图像的文件大小,因为位图图像的体积通常非常庞大。为了了解有损压缩的工作原理,让我们看看你的计算机如何对一张扫描的照片进行压缩。
对于此类文件,无损压缩程序的压缩率通常不高。尽管的大部分看起来都是相同的——例如,整个天空都是蓝色的——但是大部分像素之间都存在微小的差异。为了使变得更小同时不降低其分辨率,您必须更改某些像素的颜色值。如果中包含大量的蓝色天空,程序会挑选一种能够用于所有像素的蓝色。然后,程序重写该文件,所有天空像素的值都使用此信息。如果压缩方案选择得当,您不会注意到任何变化,但是文件大小会显著减小。
当然,对于有损压缩,在文件压缩后您无法将其复原成原始文件的样子。您必须接受压缩程序对原始文件的重新解释。因此,如果需要完全重现原来的内容(例如软件应用程序、数据库和总统就职演说),则不应该使用这种压缩形式。这是本人写的动态哈夫曼压缩算法实现,压缩与解压缩时,
根据文件内容自动生成哈夫曼树,并动态调整节点的权重
和树的形状。900MHZ的PIII赛扬每秒钟可以压缩的好几MB
的数据,只是压缩率不高,文本文件的压缩后容量一般可
以减少25%,比RAR差远了。
源文件共三个,你在VC60中新建一个空的命令行项目,
将它们加进去,编译完就可以用了。
===========hfmcpp===================
#include <stdioh>
#include <stringh>
#include <ioh>
#include <sys/stath>
#include <fcntlh>
#include "Huffmanh"
int wh;
int rh;
bool Write(unsigned char s,int len){
_write(wh,s,len);
return true;
}
bool OpenFile(char source,char target){
int w_flag=_O_WRONLY | _O_CREAT | _O_EXCL | _O_BINARY;
int r_flag=_O_RDONLY | _O_BINARY;
rh=_open(source,r_flag,_S_IREAD | _S_IWRITE);
wh=_open(target,w_flag,_S_IREAD | _S_IWRITE);
if(rh==-1 || wh==-1){
if(rh!=-1){
_close(rh);
printf("\n打开文件:'%s'失败!",target);
}
if(wh!=-1){
_close(wh);
printf("\n打开文件:'%s'失败!",source);
}
return false;
}else{
return true;
}
}
void PrintUsage(){
printf("\n以动态哈夫曼算法压缩或解压缩文件。\n\n");
printf("\thfm -\t\t\t\t显示帮助信息\n");
printf("\thfm -e -i source -o target\t压缩文件\n");
printf("\thfm -d -i source -o target\t解压缩文件\n\n");
}
void main(int argc,char args[]){
int mode,i,j,K=0;
char src[4096];
char target[4096];
unsigned char buffer[BUFFER_SIZE];
Huffman h;
mode=0;
for(i=1;i<argc;i++){
if(args[i][0]=='-' || args[i][0]=='/'){
switch(args[i][1]){
case '':
mode=0;//帮助
break;
case 'e':
case 'E':
mode=1;//压缩
break;
case 'd':
case 'D':
mode=2;//解压缩
break;
case 'o':
case 'O':
if(i+1>=argc){
mode=0;
}else{//输出文件
j=0;
while(args[i+1][j]!='\0' && j<4096){
target[j++]=args[i+1][j];
}
if(j==4096){
mode=0;
}else{
target[j]='\0';
K |= 1;
}
}
break;
case 'i':
case 'I':
if(i+1>=argc){
mode=0;
}else{//输入文件
j=0;
while(args[i+1][j]!='\0' && j<4096){
src[j++]=args[i+1][j];
}
if(j==4096){
mode=0;
}else{
src[j]='\0';
K |=2;
}
}
break;
}
}
}
if(K!=3)mode=0;
switch(mode){
case 0:
PrintUsage();
return;
case 1://压缩
if(!OpenFile(src,target))return;
h=new Huffman(&Write,true);
i=BUFFER_SIZE;
while(i==BUFFER_SIZE){
i=_read(rh,buffer,BUFFER_SIZE);
h->Encode(buffer,i);
}
delete h;
_close(rh);
_close(wh);
printf("压缩完毕!");
break;
case 2://解压缩
if(!OpenFile(src,target))return;
h=new Huffman(&Write,false);
i=BUFFER_SIZE;
while(i==BUFFER_SIZE){
i=_read(rh,buffer,BUFFER_SIZE);
h->Decode(buffer,i);
}
delete h;
_close(rh);
_close(wh);
printf("解压缩完毕!");
break;
}
}
=======end of hfmcpp=======================
=======Huffmancpp=============================
// Huffmancpp: implementation of the Huffman class
//
//////////////////////////////////////////////////////////////////////
#include "Huffmanh"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Huffman::Huffman(Output output,bool mode)
{
Hbtree tmp;
int i;
this->mode=mode;
//设置输出函数,当缓冲区满时,将调用该函数输出
this->output=output;
//初始化列表
for(i=0;i<LIST_LENGTH;i++)this->list[i]=NULL;
//初始化哈夫曼树
this->root=this->NewNode(NOT_CHAR,LEFT,NULL);
this->current=this->root;
tmp=this->NewNode(CODE_ESCAPE,RIGHT,root);
tmp->count=1;
tmp=this->NewNode(CODE_FINISH,LEFT,root);
tmp->count=0;
root->count=root->child[LEFT]->count+root->child[RIGHT]->count;
//设置缓冲区指针
this->char_top=BOTTOM_BIT;
this->bit_top=TOP_BIT;
this->buffer[0]=0;
//重构哈夫曼树的最大计数值
this->max_count=MAX_COUNT;
this->shrink_factor=SHRINK_FACTOR;
this->finished=false;
}
Huffman::~Huffman()
{
if(this->mode==true){//如果是编码
//输出结束码
this->OutputEncode(CODE_FINISH);
this->char_top++;
}
//强制清空缓冲区
this->Flush();
//释放空间
this->ReleaseNode(this->root);
}
Hbtree Huffman::NewNode(int value, int index, Hbtree parent)
{
Hbtree tmp=new Hbtree;
tmp->parent=parent;
tmp->child[0]=NULL;
tmp->child[1]=NULL;
tmp->count=(1 << SHRINK_FACTOR);
tmp->index=(index==0) 0 : 1;
tmp->value=value;
if(value!=NOT_CHAR)this->list[tmp->value]=tmp;
if(parent!=NULL)parent->child[tmp->index]=tmp;
return tmp;
}
void Huffman::ReleaseNode(Hbtree node)
{
if(node!=NULL){
this->ReleaseNode(node->child[LEFT]);
this->ReleaseNode(node->child[RIGHT]);
delete node;
}
}
//输出一位编码
int Huffman::OutputBit(int bit)
{
unsigned char candidates[]={1,2,4,8,16,32,64,128};
if(bit!=0)
this->buffer[this->char_top] |= candidates[this->bit_top];
this->bit_top--;
if(this->bit_top < BOTTOM_BIT){
this->bit_top=TOP_BIT;
this->char_top++;
if(this->char_top >= BUFFER_SIZE){//输出缓冲区
this->output(this->buffer,BUFFER_SIZE);
this->char_top=0;
}
this->buffer[this->char_top]=0;
}
return 0;
}
//输出缓冲区
int Huffman::Flush()
{
this->output(this->buffer,this->char_top);
this->char_top=0;
return 0;
}
int Huffman::Encode(unsigned char c)
{
int value=c,
candidates[]={128,64,32,16,8,4,2,1},
i;
if(this->list[value]==NULL){//字符不存在于哈夫曼树中
//输出转义码
this->OutputEncode(CODE_ESCAPE);
//输出字符
for(i=0;i<8;i++)this->OutputBit(value & candidates[i]);
this->InsertNewNode(value);
}else{
//输出字符编码
this->OutputEncode(value);
//重新调整哈夫曼树
this->BalanceNode(this->list[value]->parent);
}
//重组哈夫曼树
if(this->root->count>=this->max_count)
this->RearrangeTree();
return 0;
}
void Huffman::BalanceNode(Hbtree node)
{
Hbtree parent,child,brother;
int i,j;
parent=node->parent;
if(parent==NULL)return;//根节点无需调整
if(node->value==NOT_CHAR){//非叶子节点
child=node->child[LEFT]->count > node->child[RIGHT]->count
node->child[LEFT] : node->child[RIGHT];
if(child->count > parent->count - node->count){
//失衡
i=!(node->index);
j=child->index;
node->count=parent->count - child->count;
brother=parent->child[i];
node->child[j]=brother;
brother->index=j;
brother->parent=node;
parent->child[i]=child;
child->index=i;
child->parent=parent;
}
}
this->BalanceNode(parent);
}
//输出一个字符的编码
int Huffman::OutputEncode(int value)
{
int stack[CODE_FINISH+2],top=0;
Hbtree tmp=this->list[value];
//输出编码
if(value<=MAX_VALUE){//字符
while(tmp!=NULL){
stack[top++]=tmp->index;
tmp->count++;
tmp=tmp->parent;
}
}else{//控制码
while(tmp!=NULL){
stack[top++]=tmp->index;
tmp=tmp->parent;
}
}
top--;
while(top>0){
this->OutputBit(stack[--top]);
}
return 0;
}
void Huffman::PrintNode(Hbtree node,int level)
{
int i;
if(node){
for(i=0;i<level3;i++)printf(" ");
printf("%p P:%p L:%p R:%p C:%d",node,node->parent,node->child[0],node->child[1],node->count);
if(node->value!=NOT_CHAR)printf(" V:%d",node->value);
printf("\n");
this->PrintNode(node->child[LEFT],level+1);
this->PrintNode(node->child[RIGHT],level+1);
}
}
int Huffman::Encode(unsigned char s, int len)
{
int i;
for(i=0;i<len;i++)this->Encode(s[i]);
return 0;
}
void Huffman::PrintTree()
{
this->PrintNode(this->root,0);
}
int Huffman::RecountNode(Hbtree node)
{
if(node->value!=NOT_CHAR)return node->count;
node->count=
this->RecountNode(node->child[LEFT]) +
this->RecountNode(node->child[RIGHT]);
return node->count;
}
void Huffman::RearrangeTree()
{
int i,j,k;
Hbtree tmp,tmp2;
//所有非控制码的计数值右移shrink_factor位,并删除计数值为零的节点
for(k=0;k<=MAX_VALUE;k++){
if(this->list[k]!=NULL){
tmp=this->list[k];
tmp->count >>= this->shrink_factor;
if(tmp->count ==0){
this->list[k]=NULL;
tmp2=tmp->parent;
i=tmp2->index;
j=!(tmp->index);
if(tmp2->parent!=NULL){
tmp2->parent->child[i]=tmp2->child[j];
tmp2->child[j]->parent=tmp2->parent;
tmp2->child[j]->index=i;
}else{
this->root=tmp2->child[j];
this->current=this->root;
this->root->parent=NULL;
}
delete tmp;
delete tmp2;
}
}
}
//重新计数
this->RecountNode(this->root);
//重新调整平衡
for(i=0;i<=MAX_VALUE;i++){
if(this->list[i]!=NULL)
this->BalanceNode(this->list[i]->parent);
}
}
void Huffman::InsertNewNode(int value)
{
int i;
Hbtree tmp,tmp2;
//将字符加入哈夫曼树
tmp2=this->list[CODE_FINISH];
tmp=this->NewNode(NOT_CHAR, tmp2->index, tmp2->parent);
tmp->child[LEFT]=tmp2;
tmp2->index=LEFT;
tmp2->parent=tmp;
tmp2=this->NewNode(value,RIGHT,tmp);
tmp->count=tmp->child[LEFT]->count+tmp->child[RIGHT]->count;
i=tmp2->count;
while((tmp=tmp->parent)!=NULL)tmp->count+=i;
//从底向上调整哈夫曼树
this->BalanceNode(tmp2->parent);
}
int Huffman::Decode(unsigned char c)
{
this->Decode(c,7);
return 0;
}
int Huffman::Decode(unsigned char s,int len)
{
int i;
for(i=0;i<len;i++)this->Decode(s[i]);
return 0;
}
int Huffman::Decode(unsigned char c, int start)
{
int value=c,
candidates[]={1,2,4,8,16,32,64,128},
i,j;
Hbtree tmp;
if(this->finished)return 0;
i=start;
if(this->current==NULL){//转义状态下
while(this->remain >= 0 && i>=0){
if((candidates[i] & value) !=0){
this->literal |= candidates[this->remain];
}
this->remain--;
i--;
}
if(this->remain < 0){//字符输出完毕
//输出字符
this->OutputChar(this->literal);
//将字符插入哈夫曼树
this->InsertNewNode(literal);
//重组哈夫曼树
if(this->root->count>=this->max_count)
this->RearrangeTree();
//设置环境
this->current=this->root;
}
}else{
j=((value & candidates[i])!=0)1:0;
tmp=this->current->child[j];
i--;
while(tmp->value==NOT_CHAR && i>=0){
j=((value & candidates[i])!=0)1:0;
tmp=tmp->child[j];
i--;
}
if(tmp->value==NOT_CHAR){//中间节点
this->current=tmp;
}else{
if(tmp->value<=MAX_VALUE){//编码内容
j=tmp->value;
this->OutputChar((unsigned char)j);
//修改计数器
tmp=this->list[j];
while(tmp!=NULL){
tmp->count++;
tmp=tmp->parent;
}
//调整平衡度
this->BalanceNode(this->list[j]->parent);
//重组哈夫曼树
if(this->root->count>=this->max_count)
this->RearrangeTree();
//设置环境
this->current=this->root;
}else{
if(tmp->value==CODE_ESCAPE){//转义码
this->current=NULL;
this->remain=7;
this->literal=0;
}else{//结束码
this->finished=true;
return 0;
}
}
}
}
if(i>=0)this->Decode(c,i);
return 0;
}
int Huffman::OutputChar(unsigned char c)
{
this->buffer[this->char_top++]=c;
if(this->char_top>=BUFFER_SIZE){//输出缓冲区
this->output(this->buffer,BUFFER_SIZE);
this->char_top=0;
}
return 0;
}
========end of Huffmancpp==================
========Huffmanh============================
// Huffmanh: interface for the Huffman class
//
//////////////////////////////////////////////////////////////////////
#if !defined(NULL)
#include <stdioh>
#endif
#if !defined(AFX_HUFFMAN_H__B1F1A5A6_FB57_49B2_BB67_6D1764CC04AB__INCLUDED_)
#define AFX_HUFFMAN_H__B1F1A5A6_FB57_49B2_BB67_6D1764CC04AB__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define MAX_COUNT 65536 //最大计数值,大于此值时
#define MAX_VALUE 255 //编码的最大值
#define CODE_ESCAPE MAX_VALUE+1 //转义码
#define CODE_FINISH MAX_VALUE+2 //结束码
#define LIST_LENGTH MAX_VALUE+3 //编码列表长度
#define SHRINK_FACTOR 2 //减小的比例,通过右移位实现
#define LEFT 0 //左孩子索引
#define RIGHT 1 //右孩子索引
#define NOT_CHAR -1 //非字符
#define TOP_BIT 7 //字符最高位
#define BOTTOM_BIT 0 //字符最低位
#define BUFFER_SIZE 81920 //缓冲区大小
//输出函数
typedef bool (Output)(unsigned char s,int len);
//哈夫曼树的节点定义
typedef struct Hnode{
int count;//计数器
int index;//父节点的孩子索引(0--左孩子,1--右孩子)
Hnode child[2];
Hnode parent;
int value;
}Hbtree;
class Huffman
{
private:
//输出一个解码的字符
int OutputChar(unsigned char c);
//从指定位置开始解码
int Decode(unsigned char c,int start);
//插入一个新节点
void InsertNewNode(int value);
//重新调整哈夫曼树构型
void RearrangeTree();
//对各节点重新计数
int RecountNode(Hbtree node);
//打印哈夫曼树节点
void PrintNode(Hbtree node,int level);
//输出一个值的编码
int OutputEncode(int value);
//调节哈夫曼树节点使之平衡
void BalanceNode(Hbtree node);
//输出一位编码
int OutputBit(int bit);
//释放哈夫曼树节点
void ReleaseNode(Hbtree node);
//新建一个节点
Hbtree NewNode(int value,int index, Hbtree parent);
//输出函数地址
Output output;
//哈夫曼树根地址
Hbtree root;
//哈夫曼编码单元列表
Hbtree list[LIST_LENGTH];
//输出缓冲区
unsigned char buffer[BUFFER_SIZE];
//缓冲区顶
int char_top,bit_top;
//收缩哈夫曼树参数
int max_count,shrink_factor;
//工作模式,true--编码,false--解码
bool mode;
//解码的当前节点
Hbtree current;
int remain;//当前字符剩余的位数
unsigned char literal;//按位输出的字符
bool finished;
public:
//解码指定长度的字符串
int Decode(unsigned char s,int len);
//解码一个字符
int Decode(unsigned char c);
//打印哈夫曼树
void PrintTree();
//编码指定长度的字符串
int Encode(unsigned char s,int len);
//编码一个字符
int Encode(unsigned char c);
//清空缓冲区
int Flush();
//output指输出函数,mode指工作模式,true--编码,false--解码
Huffman(Output output,bool mode);
//析构函数
virtual ~Huffman();
};
#endif // !defined(AFX_HUFFMAN_H__B1F1A5A6_FB57_49B2_BB67_6D1764CC04AB__INCLUDED_)
================end of Huffmanh==================
祝你好运!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)