huffman算法压缩算法
A. Huffman编码不适合图像压缩么,为什么。有相关的资料么。能给我看看不QQ504278770
下面是我从网上搜索到的资料,希望对你有帮助。
1.哈夫曼图像压缩算法引言
随着网络与多媒体技术的兴起,人们需要存储和传输的数据越来越多,数据量越来越大,以前带宽有限的传输网络和容量有限的存储介质难以满足用户的需求。
特别是声音、图像和视频等媒体在人们的日常生活和工作中的地位日益突出,这个问题越发显得严重和迫切。如今,数据压缩技术早已是多媒体领域中的关键技术之一。
Huffman(哈夫曼)算法在上世纪五十年代初提出来了,它是一种无损压缩方法,在压缩过程中不会丢失信息熵,而且可以证明Huffman算法在无损压缩算法中是最优的。Huffman原理简单,实现起来也不困难,在现在的主流压缩软件得到了广泛的应用。对应用程序、重要资料等绝对不允许信息丢失的压缩场合,Huffman算法是非常好的选择。
2.哈夫曼图像压缩算法原理
Huffman编码是1952年由Huffman提出的对统计独立信源能达到最小平均码长的编码方法。这一年,他发表了着名论文“A Method for the Construction of Minimum Rendancy Codes”,即最短冗余码的构造方法.之后,Huffman编码及其一些改进方法一直是数据压缩领域的研究热点之一。
Huffman码是一种变长码,其基本思想是:先统计图像(已经数字化)中各灰度出现的概率,出现概率较大的赋以较短的码字,而出现概率较小的则赋以较长的码字。我们可以用下面的框图来表示Huffman编码的过程:
在整个编码过程中,统计图像各灰度级出现的概率和编码这两步都很简单,关键的是Huffman树的构造。不但编码的时候需要用到这颗树,解码的时候也必须有这颗树才能完成解码工作,因此,Huffman树还得完整的传输到解码端。
Huffman树的构造可以按照下面图2的流程图来完成。首先对统计出来的概率从小到大进行排序,然后将最小的两个概率相加;到这儿的时候,先把已经加过的两个概率作为树的两个节点,并把他们从概率队列中删除;然后把相加所得的新概率加入到队列中,对这个新队列进行排序。
如此反复,直到最后两个概率相加为1的时候停止。这样,Huffman树就建立起来了。
3. 哈夫曼图像压缩算法软件实现
这儿,我们以Turbo C为例来说明软件实现Huffman图像压缩算法的一些关键技术。
为了叙述方便,我们不妨假设处理的图像的灰度级变化范围从0到255,即具有256个灰度级。我们先来统计输入图像的概率,实际上是要统计各个灰度级在整幅图像中出现的次数。为此,我们先定义一个具有256个元素的数组。
然后对输入图像信号进行扫描,每出现一个灰度,就把它存入实现定义好的一个数组中的相应元素中(让这个元素的值自增1)。最后,通过读取数组中各元素的值就可以求出各个灰度出现的频数。
接下来就该构造Huffman树了。为了构造Huffman树,我们要用到C语言中链表的概念。我们必须用一个结构体来表示Huffman树的节点。对于每个节点而言我们需要这样几个信息:本节点的权重(就是灰度的频数)、指向父节点的指针和分别指向左右子叶节点的指针。于是,我们可以定义这样一个结构体:
Struct Node{
Floatweight;
Node * father;
Node * left;
Node * right;}Huffman_Node
我们需要先确定权最低的两个自由结点,这将是最初的left和right节点。然后建立这两个结点的父结点,并让它的权等于这两个结点的权之和。
接着将这个父结点增加到自由结点的序列中,而两个子结点则从序列中去掉。重复前面的步骤直到只剩下一个自由结点,这个自由结点就是Huffman树的根。
Huffman编码树作为一个二叉树从叶结点逐步向上建立。Huffman树建立好以后,为了把权、概率等数值转化码字,我们还得对整个Huffman树进行扫描。请注意,在建立Huffman树的时候,我们是从树叶开始的,而在对Huffman树分配码字的时候却刚好相反,是从树根开始,沿着各个树枝的走向“顺藤摸瓜”似的对各个系数进行编码。
对于一个节点的两个子节点(left和right),其中一个节点对应的位为0,而另一个结点则人为地设置成为l。解码的时候也是完全相同的一颗Huffman树完成的。下面的循环是实现压缩的关键语句之一[ 1 ]。
for (i = length-1; i >= 0; ――i) {
if ((current_code >> i) & 1)
thebyte |= (char) (1 << curbit);
if (--curbit < 0) {
putc (thebyte, ofile);
thebyte = 0;
curbyte++;
curbit = 7;
}
}
注意:这几行代码执行了数据压缩的功能,但是还没有生成编码和解码所需要的代码表。
4.哈夫曼图像压缩算法性能评价
我们主要从三方面[ 2 ]来评价Huffman的性能:
(1)压缩比的大小;
(2)恢复效果的好坏,也就是能否尽可能的恢复原始数据;
(3)算法的简单易用性以及编、解码的速度。
首先分析一下对压缩比的影响因素(不同的着作中对压缩比的定义不尽相同,这儿我们采用如下定义:压缩比等于压缩之前的以比特计算的数据量比上压缩之后的数据量)。对于Huffman编码来说,我们因为要用额外的位保存和传输Huffman树而“浪费”掉一些存储位,也就是说,为了编、解码的方便,我们把本已减少的数据量又增加了一些。
如果文件比较大的话,这一点多余的数据根本算不了什么,所占比例很小。但是,如果压缩的文件本来就很小的话,那么这笔数据就很可观了。一般来说,经典的Huffman算法的压缩比不是很高,这是无损压缩的“通病”。
第二点就不用说了,由于它是无损压缩,能够完全恢复压缩之前图像的本来面貌。
最后,让我们来分析一下Huffman压缩方法的速度问题。大家在第三节中已经看到了,在压缩的过程中,我们进行了两次扫描,第一次是为了统计各个灰度出现的频数而扫描整幅图像,第二次则是为了分配码字而扫描整个Huffman树。
这样一来,对较大的文件进行编码时,频繁的磁盘读写访问必然会降低数据编码的速度,如果用于网络的话,还会因此带来一些延时,不利于实时压缩和传输。另外,Huffman算法的编码和解码的速度是不对称的,解码快于编码,因为解码不需要生成Huffman树的环节。
5.图像压缩算法结束语
Huffman算法目前已经得到了广泛的应用,软件和硬件都已经实现。基于Huffman经典算法的缺陷,不少人提出了一些自适应算法。前面的算法中,Huffman树是整个图像全部输入扫描完成后构造出来的,而自适应算法(或称动态算法)则不必等到全部图像输入完成才开始树的构造,并且可以根据后面输入的数据动态的对Huffman树进行调整。实际上,实用的Huffman树都是经过某种优化后的动态算法。
网络资源
B. 基于Huffman编码的数据压缩算法的研究与实现
这是我们上学期做的一个上机题:
上机题:设电文字符集D及各字符出现的概率F如下:
D={a,b,c,d,e,f,g,h}(字符数n=8)
F={5,29,7,8,14,23,3,11}(%)
编写完成下列功能的程序:
①构造关于F的Huffman树;
②求出并打印D总各字符的Huffman编码。
程序结构: 类型说明;
构造Huffman树的函数:Huffman_tree(H[m+1]);
求Huffman编码的函数:Huffman_code(code[n+1]);
main()
{ 变量说明;
输入字符集D及频率F;
调用Huffman_tree(H);
调用Huffman_code(code);
打印编码;Y继续,N退出}
运行后,输入8个字符(中间不能有空格,否则将空格视为字符处理),然后输入概率(整数,空格或回车分隔。如果要支持浮点数,要改程序)然后Enter,出现构造的霍夫曼节点和编码,程序如下:(因为这只是一个上机题目,所以程序不复杂,也没有论文,希望对你有帮助)
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define N 8
#define M 2*N-1
#define MAX 32767
typedef char datatype;
typedef struct
{
int wi;
char data;
int Parent,Lchild,Rchild;
}huffm;
typedef struct
{
char bits[N+1];
int start;
char ch;
}ctype;
void Huffman_tree(huffm H[M+1])
{
int i,j,p1,p2;
int w,s1,s2;
for(i=1;i<=M;i++)
{
H[i].wi=MAX;
H[i].Parent=0;
H[i].Lchild=H[i].Rchild=0;
}
printf("please enter the weight:\n");
for(i=1;i<=N;i++)
{
scanf("%d",&H[i].wi);
}
for(i=N+1;i<=M;i++)
{
p1=p2=0;
s1=s2=MAX;
for(j=1;j<=M;j++)
if(H[j].Parent==0)
if(H[j].wi<s1)
{
s2=s1;
s1=H[j].wi;
p2=p1; p1=j;
}
else if(H[j].wi<s2) {s2=H[j].wi; p2=j;}
H[p1].Parent=H[p2].Parent=i;
H[i].Lchild=p1;
H[i].Rchild=p2;
H[i].wi=H[p1].wi+H[p2].wi;
}
printf("Number\tParent\tLchild\tRchild\n");
for(i=1;i<=M;i++)
printf("%d\t%d\t%d\t%d\n",i,H[i].Parent,H[i].Lchild,H[i].Rchild);
}
void Huffman_code(ctype code[N+1])
{
int i,j,p,s;
char c[N];
huffm H[M+1];
ctype md;
printf("please enter char:\n");
/* for(i=1;i<=N;i++)
{
scanf("%c",&c);
H[i].data=code[i].ch=c;
}
*/
scanf("%s",c);
for(i=1;i<=N;i++)H[i].data=code[i].ch=c[i-1];
Huffman_tree(H);
for(i=1;i<=N;i++)
{
md.ch=code[i].ch;
md.start=N+1;
s=i;
p=H[i].Parent;
while(p!=0)
{
md.start--;
if(H[p].Lchild==s)
md.bits[md.start]='1';
else
md.bits[md.start]='0';
s=p;
p=H[p].Parent;
}
code[i]=md;
}
printf("print the code:\n");
for(i=1;i<=N;i++)
printf("%c\t",code[i].ch);
printf("\n");
for(i=1;i<=N;i++)
{
for(j=code[i].start;j<=N;j++)
printf("%c",code[i].bits[j]);
printf("\t");
}
printf("\n");
}
int Continue()
{ char c;
getchar();
printf("continue? y/n\n");
c=getchar();
if(c=='y') return 1;
else return 0;
}
main()
{
do{
/* huffm H[M+1]; */
ctype code[N+1];
Huffman_code(code);
}while(Continue());
}
C. 如何用哈夫曼编码实现英文文本的压缩和解压缩
哈夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。哈夫曼压缩属于可变代码长度算法一族。意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。有人用C函数写了这个编码,见下面链接
http://ke..com/view/189694.htm
D. 如何写压缩软件,运用哈夫曼算法实现
到文件压缩大家很容易想到的就是rar,zip等我们常见的压缩格式。然而,还有一种就是大家在学习数据结构最常见到的哈夫曼树的数据结构,以前还不知道他又什么用,其实他最大的用途就是用来做压缩,也是一些rar,zip压缩的祖先,称为哈弗曼压缩(什么你不知道谁是哈弗曼,也不知道哈弗曼压缩,不急等下介绍)。
随着网络与多媒体技术的兴起,人们需要存储和传输的数据越来越多,数据量越来越大,以前带宽有限的传输网络和容量有限的存储介质难以满足用户的需求。
特别是声音、图像和视频等媒体在人们的日常生活和工作中的地位日益突出,这个问题越发显得严重和迫切。如今,数据压缩技术早已是多媒体领域中的关键技术之一。
一、什么是哈弗曼压缩
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三个二进制位。
效果显而易见。
E. 霍夫曼算法
霍夫曼算法的步骤是这样的:
·从各个节点中找出最小的两个节点,给它们建一个父节点,值为这两个节点之和。
·然后从节点序列中去除这两个节点,加入它们的父节点到序列中。
重复上面两个步骤,直到节点序列中只剩下唯一一个节点。这时一棵最优二叉树就已经建成了,它的根就是剩下的这个节点。
仍以上面的例子来看霍夫曼树的建立过程。
最初的节点序列是这样的:
a(6) b(15) c(2) d(9) e(1)
把最小的c和e结合起来
| (3)
a(6) b(15) d(9) +------+------+
| |
c e
不断重复,最终得到的树是这样的:
根
|
+-----33-----+
| |
15 +----18----+
| |
9 +------9-----+
| |
6 +--3--+
| |
2 1
这时各个字符的编码长度和前面我们说过的方法得到的编码长度是相同的,因而文件的总长度也是相同的: 3*6 + 1*15 + 4*2 + 2*9 + 4*1 = 63
考察霍夫曼树的建立过程中的每一步的节点序列的变化:
6 15 2 9 1
6 15 9 3
15 9 9
15 18
33
下面我们用逆推法来证明对于各种不同的节点序列,用霍夫曼算法建立起来的树总是一棵最优二叉树:
对霍夫曼树的建立过程运用逆推法:
当这个过程中的节点序列只有两个节点时(比如前例中的15和18),肯定是一棵最优二叉树,一个编码为0,另一个编码为1,无法再进一步优化。
然后往前步进,节点序列中不断地减少一个节点,增加两个节点,在步进过程中将始终保持是一棵最优二叉树,这是因为:
1.按照霍夫曼树的建立过程,新增的两个节点是当前节点序列中最小的两个,其他的任何两个节点的父节点都大于(或等于)这两个节点的父节点,只要前一步是最优二叉树,其他的任何两个节点的父节点就一定都处在它们的父节点的上层或同层,所以这两个节点一定处在当前二叉树的最低一层。
2.这两个新增的节点是最小的,所以无法和其他上层节点对换。符合我们前面说的最优二叉树的第一个条件。
3.只要前一步是最优二叉树,由于这两个新增的节点是最小的,即使同层有其他节点,也无法和同层其他节点重新结合,产生比它们的父节点更小的上层节点来和同层的其他节点对换。它们的父节点小于其他节点的父节点,它们又小于其他所有节点,只要前一步符合最优二叉树的第二个条件,到这一步仍将符合。
这样一步步逆推下去,在这个过程中霍夫曼树每一步都始终保持着是一棵最优二叉树。
由于每一步都从节点序列中删除两个节点,新增一个节点,霍夫曼树的建立过程共需 (原始节点数 - 1) 步,所以霍夫曼算法不失为一种精巧的编码式压缩算法。
附:对于 huffman 树,《计算机程序设计艺术》中有完全不同的证明,大意是这样的:
1.二叉编码树的内部节点(非叶子节点)数等于外部节点(叶子节点)数减1。
2.二叉编码树的外部节点的加权路径长度(值乘以路径长度)之和,等于所有内部节点值之和。(这两条都可以通过对节点数运用数学归纳法来证明,留给大家做练习。)
3.对 huffman 树的建立过程运用逆推,当只有一个内部节点时,肯定是一棵最优二叉树。
4.往前步进,新增两个最小的外部节点,它们结合在一起产生一个新的内部节点,当且仅当原先的内部节点集合是极小化的,加入这个新的内部节点后仍是极小化的。(因为最小的两个节点结合在一起,并处于最低层,相对于它们分别和其他同层或上层节点结合在一起,至少不会增加加权路径长度。)
5.随着内部节点数逐个增加,内部节点集合总维持极小化。
2.实现部分
如果世界上从没有一个压缩程序,我们看了前面的压缩原理,将有信心一定能作出一个可以压缩大多数格式、内容的数据的程序,当我们着手要做这样一个程序的时候,会发现有很多的难题需要我们去一个个解决,下面将逐个描述这些难题,并详细分析 zip 算法是如何解决这些难题的,其中很多问题带有普遍意义,比如查找匹配,比如数组排序等等,这些都是说不尽的话题,让我们深入其中,做一番思考。
F. 利用huffman树实现文件的压缩与解压
这是本人写的动态哈夫曼压缩算法实现,压缩与解压缩时,
根据文件内容自动生成哈夫曼树,并动态调整节点的权重
和树的形状。900MHZ的PIII赛扬每秒钟可以压缩的好几MB
的数据,只是压缩率不高,文本文件的压缩后容量一般可
以减少25%,比RAR差远了。
源文件共三个,你在VC6.0中新建一个空的命令行项目,
将它们加进去,编译完就可以用了。
===========hfm.cpp===================
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Huffman.h"
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 hfm.cpp=======================
=======Huffman.cpp=============================
// Huffman.cpp: implementation of the Huffman class.
//
//////////////////////////////////////////////////////////////////////
#include "Huffman.h"
//////////////////////////////////////////////////////////////////////
// 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<level*3;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 Huffman.cpp==================
========Huffman.h============================
// Huffman.h: interface for the Huffman class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(NULL)
#include <stdio.h>
#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 Huffman.h==================
祝你好运!
G. 霍夫曼算法是否只适合压缩小型文件(100MB以下的),为什么用它压缩500多MB文件,时间都够我吃两顿饭了
霍夫曼算法不是一个流式压缩算法,需要统计整个文件的所有信息,才能开始压缩,所以速度上不行。一般的流式压缩算法,例如LZMA,他只要一段文件的局部信息就可以进行压缩了,所以速度上有优势。而正因为它用的局部信息,压缩比又不如霍夫曼。
正所谓凡事有利必有弊,速度快的算法,压缩比不够高,压缩比高的算法,速度上又不行。计算机编程之难点,不是什么高深的算法,而是要根据需求找到事物之间的平衡点,选取最适合的策略。
H. 跪求哈夫曼编码压缩与其它压缩算法的比较(复杂性和压缩效果)
(1)所形成的Huffman编码的码字是不是唯一的,但是可以被指定为唯一的编码效率为“1”大,小的是“0”时,两个最小概率符号赋值。反之也可以。如果两个符号的发生的概率是相等的,排列无论前面是可能的,所以霍夫曼码字的结构不是唯一的,对于相同的信息源,不管如何在上述的顺序安排的,它的平均码字长度是不改变,因此,编码效率是独一无二的。
(2)只有当不均匀时,每个符号的信息源的发生的概率,霍夫曼编码的效果是唯一明显的。
(3)霍夫曼编码必须是精确的原始文件中的各符号的发生频率的统计数据,并且如果没有准确的统计数据,压缩将低于预期。 Huffman编码通常必须经过两道,第一遍统计的第二次产生编码,编码速度是比较慢的。电路的复杂性的另一种实现的各种长度的编码,解码处理是相对复杂的,因此,解压缩处理是相对缓慢。
(4)Huffman编码只能使用整数来表示一个符号,而不是使用小数,这在很大程度上限制了压缩效果。
(5)霍夫曼是所有的位,如果改变其中一个可以使数据看起来完全不同
I. 哈夫曼压缩算法的内容是什么
注:哈夫曼和lzss算法不是同一种算法,先用哈夫曼再用lzss算法压缩后会发现经哈夫曼压缩后再用lzss压缩文件会变大,具体原因不明
lzss原理:
把编码位置置于输入数据流的开始位置。
在前向缓冲器中查找窗口中最长的匹配串
①
pointer
:=匹配串指针。
②
length
:=匹配串长度。
判断匹配串长度length是否大于等于最小匹配串长度(min_length)
,
如果“是”:输出指针,然后把编码位置向前移动length个字符。
如果“否”:输出前向缓冲存储器中的第1个字符,然后把编码位置向前移动一个字符。
如果前向缓冲器不是空的,就返回到步骤2。
例:编码字符串如表03-05-3所示,编码过程如表03-05-4所示。现说明如下:
“步骤”栏表示编码步骤。
“位置”栏表示编码位置,输入数据流中的第1个字符为编码位置1。
“匹配”栏表示窗口中找到的最长的匹配串。
“字符”栏表示匹配之后在前向缓冲存储器中的第1个字符。
“输出”栏的输出为:
①
如果匹配串本身的长度length
>=
min_length,输出指向匹配串的指针,格式为(back_chars,
chars_length)。该指针告诉译码器“在这个窗口中向后退back_chars个字符然后拷贝chars_length个字符到输出”。
②
如果匹配串本身的长度length
>=
min_length,则输出真实的匹配串。
表:输入数据流
位置
1234567891011
字符
aabbcbbaabc
表:编码过程(min_length
=
2)
步骤位置匹配串输出
11--a
22aa
33--
b
44bb
55--c
66b
b(3,2)
78
a
a
b(7,3)
811cc