哈夫曼编码HuffmanCoding.docx
《哈夫曼编码HuffmanCoding.docx》由会员分享,可在线阅读,更多相关《哈夫曼编码HuffmanCoding.docx(14页珍藏版)》请在冰豆网上搜索。
![哈夫曼编码HuffmanCoding.docx](https://file1.bdocx.com/fileroot1/2023-1/9/a57ea704-0742-47a1-a9b4-42718ba27e8d/a57ea704-0742-47a1-a9b4-42718ba27e8d1.gif)
哈夫曼编码HuffmanCoding
哈夫曼编码
哈夫曼编码(HuffmanCoding)是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。
Huffman于1952年提出一种编码方式,该方式完全依据字符显现概率来构造异字头的平均长度最短的码字,有时称之为最正确编码,一样就叫作Huffman编码。
以─即最优二叉树,带权途径长度最小的二叉树,常常应用于数据紧缩。
在运算机信息处置中,“哈夫曼编码”是一种一致性编码法(又称"熵编码法"),用于数据的无损耗紧缩。
这一术语是指利用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。
这张编码表的特殊的地方在于,它是依照每一个源字符显现的估算概率而成立起来的(显现概率高的字符利用较短的编码,反之显现概率低的那么利用较长的编码,这便使编码以后的字符串的平均期望长度降低,从而达到无损紧缩数据的目的)。
这种方式是由进展起来的。
例如,在英文中,e的显现概率很高,而z的显现概率那么最低。
当利用哈夫曼编码对一篇英文进行紧缩时,e极有可能用一个位(bit)来表示,而z那么可能花去25个位(不是26)。
用一般的表示方式时,每一个英文字母均占用一个字节(byte),即8个位。
二者相较,e利用了一样编码的1/8的长度,z那么利用了3倍多。
倘假设咱们能实现关于英文中各个字母显现概率的较准确的估算,就能够够大幅度提高无损紧缩的比例。
本文描述在网上能够找到的最简单,最快速的哈夫曼编码。
本方式不利用任何扩展动态库,比如STL或组件。
只利用简单的C函数,比如:
memset,memmove,qsort,malloc,realloc和memcpy。
因此,大伙儿都会发觉,明白得乃至修改那个编码都是很容易的。
背景
哈夫曼紧缩是个无损的紧缩算法,一样用来紧缩文本和程序文件。
哈夫曼紧缩属于可变代码长度算法一族。
意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。
因此,在文件中显现频率高的符号,利用短的位序列,而那些很少显现的符号,那么用较长的位序列。
编码利用
我用简单的C函数写那个编码是为了让它在任何地址利用都会比较方便。
你能够将他们放到类中,或直接利用那个函数。
而且我利用了简单的格式,仅仅输入输出缓冲区,而不象其它文章中那样,输入输出文件。
boolCompressHuffman(BYTE*pSrc,intnSrcLen,BYTE*&pDes,int&nDesLen);
boolDecompressHuffman(BYTE*pSrc,intnSrcLen,BYTE*&pDes,int&nDesLen);
要点说明
速度
为了让它快速运行,我花了很长时刻。
同时,我没有利用任何动态库,比如STL或MFC。
它紧缩1M数据少于100ms(P3处置器,主频1G)。
紧缩
紧缩代码超级简单,第一用ASCII值初始化511个哈夫曼节点:
CHuffmanNodenodes[511];
for(intnCount=0;nCount<256;nCount++)
nodes[nCount].byAscii=nCount;
然后,计算在输入缓冲区数据中,每一个ASCII码显现的频率:
for(nCount=0;nCount nodes[pSrc[nCount]].nFrequency++;
然后,依照频率进行排序:
qsort(nodes,256,sizeof(CHuffmanNode),frequencyCompare);
此刻,构造哈夫曼树,获取每一个ASCII码对应的位序列:
intnNodeCount=GetHuffmanTree(nodes);
构造哈夫曼树超级简单,将所有的节点放到一个队列中,用一个节点替换两个频率最低的节点,新节点的频率确实是这两个节点的频率之和。
如此,新节点确实是两个被替换节点的父节点了。
如此循环,直到队列中只剩一个节点(树根)。
wCode<<(nDesIndex&7);
nDesIndex+=nodes[pSrc[nCount]].nCodeLength;
}
(nDesIndex>>3):
>>3以8位为界限右移后抵达右边字节的前面
(nDesIndex&7):
&7取得最高位.
注意:
在紧缩缓冲区中,咱们必需保留哈夫曼树的节点和位序列,如此咱们才能在解紧缩时从头构造哈夫曼树(只需保留ASCII值和对应的位序列)。
解紧缩
解紧缩比构造哈夫曼树要简单的多,将输入缓冲区中的每一个编码用对应的ASCII码逐个替换就能够够了。
只要记住,那个地址的输入缓冲区是一个包括每一个ASCII值的编码的位流。
因此,为了用ASCII值替换编码,咱们必需用位流搜索哈夫曼树,直到发觉一个叶节点,然后将它的ASCII值添加到输出缓冲区中:
intnDesIndex=0;
DWORDnCode;
while(nDesIndex {
nCode=(*(DWORD*)(pSrc+(nSrcIndex>>3)))>>(nSrcIndex&7);
pNode=pRoot;
while(pNode->pLeft)
{
pNode=(nCode&1)?
pNode->pRight:
pNode->pLeft;
nCode>>=1;
nSrcIndex++;
}
pDes[nDesIndex++]=pNode->byAscii;
}
进程
#include<>
#include<>
#include<>
#include<>
#include<>
#defineM10
typedefstructFano_Node
{
charch;
floatweight;
}FanoNode[M];
typedefstructnode
{
intstart;
intend;
structnode*next;
}LinkQueueNode;
typedefstruct
{
LinkQueueNode*front;
LinkQueueNode*rear;
}LinkQueue;
voidEnterQueue(LinkQueue*q,ints,inte)
{
LinkQueueNode*NewNode;
NewNode=(LinkQueueNode*)malloc(sizeof(LinkQueueNode));
if(NewNode!
=NULL)
{
NewNode->start=s;
NewNode->end=e;
NewNode->next=NULL;
q->rear->next=NewNode;
q->rear=NewNode;
}
elseprintf("Error!
");
}
h)
{
printf("Samenode\n");
break;
}
}
if(i==j)
i++;
}
for(i=1;i<=n;i++)/*排序*/
{
max=i+1;
for(j=max;j<=n;j++)
max=FN[max].weightj:
max;
if {
w=;
=FN[max].weight;
FN[max].weight=w;
c=;
=FN[max].ch;
FN[max].ch=c;
}
}
for(i=1;i<=n;i++)/*初始化h*/
h=0;
EnterQueue(Q,1,n);/*1和n进队*/
while(Q->front->next!
=NULL)
{
p=Q->front->next;/*出队*/
Q->front->next=p->next;
if(p==Q->rear)
Q->rear=Q->front;
sta=p->start;
end=p->end;
free(p);
Divide(FN,sta,&m,end);/*按权分组*/
for(i=sta;i<=m;i++)
{
fc[h]='0';
h++;
}
if(sta!
=m)
EnterQueue(Q,sta,m);
else
fc[sta][h[sta]]='\0';
for(i=m+1;i<=end;i++)
{
fc[h]='1';
h++;
}
if(m==sta&&(m+1)==end)=ch;
(*CW)[*p].weight=1;
for(k=i+1;ch[k]!
='\0';k++)
if(ch==ch[k])
(*CW)[*p].weight++;
}
}
*s=i;
}
/********创建HuffmanTree********/
voidCreateHuffmanTree(Huffman*ht,WeightNodew,intn)
{
inti,j;
ints1,s2;
for(i=1;i<=n;i++)
{
(*ht).weight=;
(*ht).parent=0;
(*ht).LChild=0;
(*ht).RChild=0;
}
for(i=n+1;i<=2*n-1;i++)
{
(*ht).weight=0;
(*ht).parent=0;
(*ht).LChild=0;
(*ht).parent=0;
}
for(i=n+1;i<=2*n-1;i++)
{
for(j=1;j<=i-1;j++)
if(!
(*ht)[j].parent)
break;
s1=j;/*找到第一个双亲不为零的结点*/
for(;j<=i-1;j++)
if(!
(*ht)[j].parent)
s1=(*ht)[s1].weight>(*ht)[j].weight?
j:
s1;
(*ht)[s1].parent=i;
(*ht).LChild=s1;
for(j=1;j<=i-1;j++)
if(!
(*ht)[j].parent)
break;
s2=j;/*找到第一个双亲不为零的结点*/
for(;j<=i-1;j++)
if(!
(*ht)[j].parent)
s2=(*ht)[s2].weight>(*ht)[j].weight?
j:
s2;
(*ht)[s2].parent=i;
(*ht).RChild=s2;
(*ht).weight=(*ht)[s1].weight+(*ht)[s2].weight;
}
}
/***********叶子结点的编码***********/
voidCrtHuffmanNodeCode(Huffmanht,charch[],HuffmanCode*h,WeightNode*weight,intm,intn)
{
inti,j,k,c,p,start;
char*cd;
cd=(char*)malloc(n*sizeof(char));
cd[n-1]='\0';
for(i=1;i<=n;i++)
{
start=n-1;
c=i;
p=;
while(p)
{
start--;
if(ht[p].LChild==c)
cd[start]='0';
else
cd[start]='1';
c=p;
p=ht[p].parent;
}
(*weight).num=n-start;
(*h)=(char*)malloc((n-start)*sizeof(char));
p=-1;
strcpy((*h),&cd[start]);
}
system("pause");
}
/*********所有字符的编码*********/
voidCrtHuffmanCode(charch[],HuffmanCodeh,HuffmanCode*hc,WeightNodeweight,intn,intm)
{
inti,j,k;
for(i=0;i {
for(k=1;k<=n;k++)/*从(*weight)[k].c中查找与ch相等的下标K*/
if(ch==weight[k].c)
break;
(*hc)=(char*)malloc((weight[k].num+1)*sizeof(char));
for(j=0;j<=weight[k].num;j++)
(*hc)[j]=h[k][j];
}
}
/*****解码*****/
voidTrsHuffmanTree(Huffmanht,WeightNodew,HuffmanCodehc,intn,intm)
{
inti=0,j,p;
printf("***StringInformation***\n");
while(i {
p=2*n-1;
for(j=0;hc[j]!
='\0';j++)
{
if(hc[j]=='0')
p=ht[p].LChild;
else
p=ht[p].RChild;
}
printf("%c",w[p].c);/*打印原信息*/
i++;
}
}
main()
{
inti,n,m,s1,s2,j;/*n为叶子结点的个数*/
charch[N],w[N];/*ch[N]寄存输入的字符串*/
Huffmanht;/*二叉数*/
HuffmanCodeh,hc;/*h寄存叶子结点的编码,hc寄存所有结点的编码*/
WeightNodeweight;/*寄存叶子结点的信息*/
printf("\t***HuffmanCoding***\n");
printf("pleaseinputinformation:
");
gets(ch);/*输入字符串*/
CreateWeight(ch,&m,&weight,&n);/*产生叶子结点信息,m为字符串ch[]的长度*/
printf("***WeightInformation***\nNode");/*输出叶子结点的字符与权值*/
for(i=1;i<=n;i++)
printf("%c",;
printf("\nWeight");
for(i=1;i<=n;i++)
printf("%d",;
CreateHuffmanTree(&ht,weight,n);/*产生Huffman树*/
printf("\n***HuffamnTreeInformation***\n");
for(i=1;i<=2*n-1;i++)/*打印Huffman树的信息*/
printf("\t%d%d%d%d\n",i,,,,;
CrtHuffmanNodeCode(ht,ch,&h,&weight,m,n);/*叶子结点的编码*/
printf("***NodeCode***\n");/*打印叶子结点的编码*/
for(i=1;i<=n;i++)
{
printf("\t%c:
",;
printf("%s\n",h);
}
CrtHuffmanCode(ch,h,&hc,weight,n,m);/*所有字符的编码*/
printf("***StringCode***\n");/*打印字符串的编码*/
for(i=0;i printf("%s",hc);
system("pause");
TrsHuffmanTree(ht,weight,hc,n,m);/*解码*/
system("pause");
}
Matlab中简易实现Huffman编译码:
n=input('Pleaseinputthetotalnumber:
');
hf=zeros(2*n-1,5);
hq=[];
forki=1:
n
hf(ki,1)=ki;
hf(ki,2)=input('Pleaseinputthefrequency:
');
hq=[hq,hf(ki,2)];
end
forki=n+1:
2*n-1
hf(ki,1)=ki;
mhq1=min(hq);
m=size(hq);
m=m(:
2);
k=1;
whilek<=m%delmin1
ifhq(:
k)==mhq1
hq=[hq(:
1:
(k-1))hq(:
(k+1):
m)];
m=m-1;
break
else
k=k+1;
end
end
k=1;
whilehf(k,2)~=mhq1|hf(k,5)==1%findmin1location
k=k+1;
end
hf(k,5)=1;
k1=k;
mhq2=min(hq);
k=1;
whilek<=m%delmin2
ifhq(:
k)==mhq2
hq=[hq(:
1:
(k-1))hq(:
(k+1):
m)];
m=m-1;
break
else
k=k+1;
end
end
k=1;
whilehf(k,2)~=mhq2|hf(k,5)==1%findmin2location
k=k+1;
end
hf(k,5)=1;
k2=k;
hf(ki,2)=mhq1+mhq2;
hf(ki,3)=k1;
hf(ki,4)=k2;
hq=[hqhf(ki,2)];
end
clc
choose=input('Pleasechoosewhatyouwant:
\n1:
Encoding\n2:
Decoding\n3:
.Exit\n');
whilechoose==1|choose==2
ifchoose==1
a=input('PleaseinputtheletteryouwanttoEncoding:
');
k=1;
whilehf(k,2)~=a
k=k+1;
ifk>=n
display('Error!
Youdidnotinputthisnumber.');
break
end
end
ifk>=n
break
end
r=[];
whilehf(k,5)==1
kc=n+1;
whilehf(kc,3)~=k&hf(kc,4)~=k
kc=kc+1;
end
ifhf(kc,3)==k
r=[0r];
else
r=[1r];
end
k=kc;
end
r
else
a=input('PleaseinputthemetrixyouwanttoDecoding:
');
sa=size(a);
sa=sa(:
2);
k=2*n-1;
whilesa~=0
ifa(:
1)==0
k=hf(k,3);
else
k=hf(k,4);
end
a=a(:
2:
sa);
sa=sa-1;
ifk==0
display('Error!
Themetrixyouenteredisawrongone.');
break
end
end
ifk==0
break
end
r=hf(k,2);
r
end
choose=input('Pleasechoosewhatyouwant:
\n1:
Encoding\n2:
Decoding\n3:
.Exit\n');
clc
end
ifchoose~=1&choose~=2
clc;
end