您的位置:首页 > 理论基础 > 数据结构算法

重读数据结构之-- 霍夫曼编码

2013-12-07 00:00 423 查看
11.霍夫曼编码

①统计一篇文章各个字母出现的频率作为权值,字母作为叶子结点,造成一个霍夫曼树,并把这棵树左分支改为0,右分支改为1.每个字母从根开始的路径01编码作为该字母的编码。

②前缀编码:如果要设计长短不等的编码,则必须是任一字符的编码都不是另一个字符编码的前缀。利用霍夫曼树生成的各个字母结点的编码就属于前缀编码。

③霍夫曼编码定义: 设需要编码的字符集为{d1,d2...dn},各个字符在电文中出现的次数或频率定义为集合{w1,w2...wn},以dn作为叶子结点,以wn作为叶子权值构成一颗霍夫曼树 规定该霍夫曼树左分支代表0,右分支代表1,则从根节点所经过的路径分支组成的01序列便是该结点对应字符的编码。

④代码实现

/*------------------------霍夫曼编码的实现---------------------------*/
#define N 100
#define M 2*N-1
typedef char *HuffmanCode[2*M]; //存放编码的01序列

typedef struct{//定义霍夫曼树上的结点,和结点组成的树
int weight;
int parent;
int LChild;
int RChild;
}HTNode,Huffman[M+1];

typedef struct Node{//定义字符所代表的叶子结点
int weight;
char c;
int num;//叶子结点的二进制码的长度
}WNode,WeightNode
;

/*--------------产生叶子结点的字符和权值-------------*/
void CreateWeight(char ch[],int *s,WeightNode CW,int *p){
int i,j,k;
int tag;
*p=0;
/*----统计各字符出现的次数,结果放入CW----*/
for(i=0;ch[i]!='\0';i++){
tag=1;
for(j=0;j<i;j++){
if(ch[j]==ch[i]){//此处代码用来过滤重复,每次都只针对一个新字符进行权值计算,一次计算完毕,下次不再对重复的字符统计次数
tag=0;
break;
}
if(tag){//确定此时的字符是还没统计过的字符
CW[++*p].c=ch[i];//记录字符值
CW[*p].weight=1;//出现了一次,统计一下
for(k=i+1;ch[k]!='\0';k++){//要是后面还再出现,则权值累加
if(ch[j]==ch[k]){
CW[*p].weight++; //权值累加
}
}
}
}
//至此,一个字符的统计工作完全结束,进入下一个字符
*s=i;//字符串的长度
}
/*----统计各字符出现的次数,结果放入CW----*/
}

/*--------------产生叶子结点的字符和权值-------------*/

/*------------------创建霍夫曼树---------------------------*/
void CreateHuffmanTree(Huffman ht,WeightNode w,int n){
int i,j;
int s1,s2;
for(i=1;i<=n;i++){//遍历待创建树的所有叶子,对叶子结点进行整体的初始化
ht[i].weight=w[i].weight;
ht[i].parent=0;
ht[i].LChild=0;
ht[i].RChild=0;
}
for(i=n+1;i<=2*n-1;i++){//遍历剩余的非叶子结点,进行初始化
ht[i].weight=0;
ht[i].parent=0;
ht[i].LChild=0;
ht[i].RChild=0;
}
for(i=n+1;i<=2*n-1;i++){

for(j=1;j<=i-1;j++){
if(!ht[j].parent)
break;
}
s1=j;//找到第一个双亲不为0的结点
for(;j<=i-1;j++){//从第一个双亲不为0结点开始往后找双亲为0的权值最小结点
if(!ht[j].parent){//如果此时结点没有双亲
s1=ht[s1].weight>ht[j].weight?j:s1;//权值最小的且没有双亲的结点的角标
}
}
ht[s1].parent=i;//给这个结点指定一个双亲
ht[i].LChild=s1;//把这个结点作为其双亲i的左孩子

for(j=1;j<=i-1;j++){
if(!ht[j].parent)
break;
}
s2=j;//找到第二个双亲不为0的结点

for(;j<=i-1;j++){
if(!ht[j].parent){
s2=ht[s2].weight>ht[j].weight?j:s2;//继续找下一个双亲为0的权值最小结点的角标
}
}
ht[s2].parent=i;//给第二个权值最小的结点赋值
ht[i].RChild=s2;//把这个结点作为其双亲i的右孩子
ht[i].weight=ht[s1].weight+ht[s2].weight;//新结点的权值是两个孩子的和

}
}
/*------------------创建霍夫曼树---------------------------*/

/*-------求树的每一个叶子的编码值--------*/
void CrtHuffmanNodeCode(Huffman ht,HuffmanCode h,WeightNode weight,int m,int n){
int i,c,p,start;
char *cd;
cd=(char*)malloc(n*sizeof(char));//创建字符数组用来存放编码值
cd[n-1]='\0';//字符串结尾标记
for(i=1;i<=n;i++){
start=n-1;//cd字符串每次都从末尾开始,即从叶子出发去寻根,路径值从后开始放
c=i;   //记录当前要寻根的值
p=ht[i].parent;//p在n+1到2n-1之间
while(p){ //寻根开始
start--;
if(ht[p].LChild==c){//如果i是其双亲的左孩子
cd[start]='0';//左树为0
}else{
cd[start]='1';//右树为1
}
c=p;//下次要寻根的是p了
p=ht[p].parent;//获取p的双亲,进入下一次的循环
}
weight[i].num=n-start;//最终确定i结点编码后的01序列长度值
h[i]=(char*)malloc((n-start)*sizeof(char));//给这个长度的01序列分配一个数组空间
strcpy(h[i],&cd[start]);//cd[start]是原来存放编码值数组的第一个元素,也是数组的指针

//--->存放所有叶子结点的数组weight和存放所有叶子结点序列长度的数组h是一一对应的!!!!
}
free(cd);
system("pause");
}
/*-------求树的某一个叶子的编码值--------*/

/*-----求这篇文章中所有字符的编码结果--------*/
void CrtHuffmanCode(char ch[],HuffmanCode h,HuffmanCode hc,WeightNode weight,int m,int n){
int i,k;
for(i=0;i<m;i++){
for(k=1;k<=n;k++){
if(ch[i]==weight[k].c){//参阅码表对应找文章的字符
break;
}
}
hc[i]=(char*)malloc(weight[k].num*sizeof(char));//找到一个,则为某个叶子分配对应它路径长度的字符数组
strcpy(hc[i],h[k]);//本篇文章中所有字符码表存在hc
}
}

/*-----求这篇文章中所有字符的编码结果--------*/

/*--------解码---------*/
//ht就是一个码表,hc是文章全部编码完毕后的字符串,w是解码真值对照表
void TrsHuffmanTree(Huffman ht,WeightNode w,HuffmanCode hc,int m,int n){
int i=0,j,p;
printf("-----字符串解码信息----\n");
while(i<m){//i代表遍历编码完毕后的文章的第i个字符
p=2*n-1;//根节点
for(j=0;hc[i][j]!='\0';j++){  //针对原文以字符,开始遍历这个字符对应的01序列,遍历完毕也就可以找到在码表ht中是哪个叶子了,叶子里有字符解码值
if(hc[i][j]=='0')
p=ht[p].LChild; //左孩子的位置
else
p=ht[p].RChild;
}
printf("%c",w[p].c);//最终叶子的.c就是取叶子真正的值,也就是解码后的值
i++;//下一条待解码字符串
}
}

/*--------解码---------*/
#if 0
/*主函数*/
void main(){
int i;
int n=0;//叶子结点的个数
int m=0;//文章的长度
char ch
;//文章
Huffman ht;//树
HuffmanCode hc,h;//hc存放所有结点的编码,h存放叶子结点的编码
WeightNode weight;//叶子结点
printf("输入待编码字符:\n");
gets(ch);
CreateWeight(ch,&m,weight,&n);//计算文章中每个字母的权值和出现字母个数
printf("结点信息:\n");
for(i=1;i<=n;i++){
printf("%c",weight[i].c);
}
printf("对应权值信息:\n");
for(i=1;i<=n;i++){
printf("%c",weight[i].weight);
}
CreateHuffmanTree(ht,weight,n);//利用权值结点创建树
CrtHuffmanNodeCode(ht,h,weight,m,n);//把树的叶子的编码结果存入h中
CrtHuffmanCode(ch,h,hc,weight,m,n);//把整片文章的字符都替换成编码,放入数组hc
TrsHuffmanTree(ht,weight,hc,m,n);//解码就是队原文章每个字符的01序列遍历寻找树中对应的是哪个叶子
//释放内存代码省略.....
}
/*主函数*/
/*------------------------霍夫曼编码的实现---------------------------*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: