霍夫曼编码总结
作者:CC Time:2010-1-9
霍夫曼(Huffman)编码是1952年为文本文件而建立,是一种统计编码。属于无损压缩编码。
霍夫曼编码的码长是变化的,对于出现频率高的信息,编码的长度较短;而对于出现频率低的信息,编码长度较长。这样,处理全部信息的总码长一定小于实际信息的符号长度。
霍夫曼计算法步骤进行:
l)将信号源的符号按照出现概率递减的顺序排列。
2)将两个最小出现概率进行合并相加,得到的结果作为新符号的出现概率。
3)重复进行步骤1和2直到概率相加的结果等于1为止。
4)在合并运算时,概率大的符号用编码0表示,概率小的符号用编码1表示。
5)记录下概率为1处到当前信号源符号之间的0,l序列,从而得到每个符号的编码。
霍夫曼计算法举例:
霍夫曼优点:
霍夫曼码的码长虽然是可变的,但却自带同步代码。例如,码串中的第1位为0,那末肯定是符号A,因为表示其他符号的代码没有一个是以0开始的,因此下一位就表示下一个符号代码的第1位。同样,如果出现“110”,那么它就代表符号D。如果事先编写出一本解释各种代码意义的“词典”,即码簿,那么就可以根据码簿一个码一个码地依次进行译码。
霍夫曼编码方法的编码效率比香农-范诺编码效率高一些
霍夫曼缺点:
① 霍夫曼码没有错误保护功能,在译码时,如果码串中没有错误,那么就能一个接一个地正确译出代码。但如果码串中有错误,哪仅是1位出现错误,不但这个码本身译错,更糟糕的是一错一大串,全乱了套,这种现象称为错误传播(error propagation)。计算机对这种错误也无能为力,说不出错在哪里,更谈不上去纠正它。
② 霍夫曼码是可变长度码,因此很难随意查找或调用压缩文件中间的内容,然后再译码,这就需要在存储代码之前加以考虑。硬件实现有难度。
③ 由于"0"与"1"的指定是任意的,故由上述过程编出的最佳码不是唯一的,但其平均码长是一样的,故不影响编码效率与数据压缩性能。
④ 压缩与还原相当费时。
⑤ 对不同信号源的编码效率不同
霍夫曼C语言实现:
#includestdio.h>
#includestdlib.h>
#includestring.h>
#includemalloc.h>
#define N 20
#define M 2*N-1
#define Min 1000 /*最小值*/
typedef struct
{
char ch; /*权值对应的字符*/
int weight; /*权值*/
int parent; /*双亲位置*/
int Lchild; /*左孩子位置*/
int Rchild; /*右孩子位置*/
}HTNode,Huffmantree[M+1];
char hc[N+1][N+1];
void OutputHuffmancode(Huffmantree ht, int n);
void CrtHuffmancode(Huffmantree ht, int n);
void OutputHuffmantree(Huffmantree ht,int n);
void select(Huffmantree ht,int a,int *s1,int *s2)
{
int i;
int c1=Min;
int c2;
for(i=1;i=a;i++)
{
if (ht.parent==0&&ht.weightc1)
{
*s1=i;
c1=ht.weight;
}
}
c2=Min;
for(i=1;i=a;i++)
{
if (ht.parent==0&&(*s1!=i)&&c2>ht.weight)
{
*s2=i;
c2=ht.weight;
}
}
}
void CrtHuffmantree(Huffmantree ht,int w[],char elem[],int n)
{
int i;
int m;
int s1,s2;
m=2*n-1;
ht=(HTNode *)malloc((m)*sizeof(HTNode));
for(i=1;i=n;i++)
{
ht.ch=elem[i-1];
ht.weight=w[i-1];
ht.parent=0;
ht.Lchild=0;
ht.Rchild=0;
}
for(i=n+1;i=m;i++)
{
ht.ch='/0';
ht.weight=0;
ht.parent=0;
ht.Lchild=0;
ht.Rchild=0;
}
/*初始化完毕*/
for(i=n+1;i=m;i++)
{
select( ht,i-1,&s1,&s2); /*返回最小值和次小值的位置*/
ht.weight=ht[s1].weight+ht[s2].weight;
ht[s1].parent=i;
ht[s2].parent=i;
ht.Lchild=s1;
ht.Rchild=s2;/*建立树完毕*/
}
OutputHuffmantree( ht,m);
printf("now begin crthuffman code/n");
CrtHuffmancode( ht, n);
printf("crthuffman code end/n");
OutputHuffmancode(ht, n);
}
void OutputHuffmantree(Huffmantree ht,int n)
{
int i;
printf("/nnumber---weight---parent---Lchild---Rchild---huffman char/n /n");
for(i=1;i=n;i++)
printf("%d/t%d/t%d/t%d/t%d/t%c/n",i,ht.weight,ht.parent,ht.Lchild,ht.Rchild,ht.ch);
}
void CrtHuffmancode(Huffmantree ht, int n) /*建立编码*/
{
int i,c,p;
int start;
char *cd;
cd=(char *) malloc((n+1)*sizeof(char));
memset(cd,'/0',sizeof(cd));
for(i=1;i=n;i++)
{
start=n;
cd[start]='/0';
c=i;
p=ht.parent;
while(p!=0)
{
start--;
if(ht[p].Lchild==c)
cd[start]='0';
else
cd[start]='1';
c=p;
p=ht[p].parent;
}
//cd[start] = '/0';
printf("cd is %s/n start is %d/n", cd+start, start);
sprintf(hc, "%s", cd+start); /*将已存在的编码复制到code中*/
}
free(cd);
}
void OutputHuffmancode(Huffmantree ht,int n)
{
int i;
printf("/nweight_char---weight---huffmancode/n /n");
for(i=1;i=n;i++)
printf(" %c/t%4d/t%s/n",ht.ch,ht.weight,hc);
}
int main()
{
int n = 6;/*记录了权值个数*/
Huffmantree hfm;
int w[] = {45,13,12,16,9,5};
char elem[] = {'a','b','c','d','e','f'};
CrtHuffmantree( hfm, w,elem, n);
return 0;
}
霍夫曼C++语言实现:
//以下為C++程式碼,在GCC下編譯通過
//僅用於示範如何根據權值構建霍夫曼樹,沒有經過性能上的優化及加上完善的異常處理。
#include <cstdlib>
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
const int size=10;
struct node //霍夫曼樹節點結構
{
unsigned key; //保存權值
node* lchild; //左孩子指針
node* rchild; //右孩子指針
};
deque<node*> forest;
deque<bool> code; //此處也可使用bitset
node* ptr;
int frequency[size]={0};
void printCode(deque<bool> ptr); //用於輸出霍夫曼編碼
int main(int argc, char *argv[])
{
for (int i=0;i<size;i++)
{
cin>>frequency[i]; //輸入10個權值
ptr=new node;
ptr->key=frequency[i];
ptr->lchild=NULL;
ptr->rchild=NULL;
forest.push_back(ptr);
}//形成森林,森林中的每一棵樹都是一個節點
//從森林構建霍夫曼樹
for (int i=0;i<size-1;i++)
{
sort(forest.begin(),forest.end(),compare);
ptr=new node;
//以下代碼使用下標索引隊列元素,具有潛在危險,使用時請注意
ptr->key=forest[0]->key+forest[1]->key;
ptr->lchild=forest[0];
ptr->rchild=forest[1];
forest.pop_front();
forest.pop_front();
forest.push_back(ptr);
}
ptr=forest.front();//ptr是一個指向根的指針
system("PAUSE");
return EXIT_SUCCESS;
}
void printCode(deque<bool> ptr)
{
//deque<bool>
for (int i=0;i<ptr.size();i++)
{
if(ptr[i])
cout<<"1";
else
cout<<"0";
}
}