哈夫曼树编码译码.docx
《哈夫曼树编码译码.docx》由会员分享,可在线阅读,更多相关《哈夫曼树编码译码.docx(21页珍藏版)》请在冰豆网上搜索。
![哈夫曼树编码译码.docx](https://file1.bdocx.com/fileroot1/2022-11/27/8d735d75-92be-475a-b070-f9d8a2e8edcf/8d735d75-92be-475a-b070-f9d8a2e8edcf1.gif)
哈夫曼树编码译码
实验三哈夫曼编码/译码器
1问题
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本,但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。
对于双工信道(即可以双向传输信息的信道)每端都需要一个完整的编/译码系
统。
试为这样的信息收发站写一哈夫曼编/译码系统。
基本要求:
(1)初始化;从终端输入字符集的大小n,以及n个字符和n个权值建立哈夫曼树。
(2)输出哈夫曼树,及各字符对应的编码。
(3)编码:
利用建好的哈夫曼树,对输入的待发送电文进行编码。
同时输入原文及编码串。
(4)译码:
利用建好的哈夫曼树,对输入的已接收电文进行译码。
同时输入编码串及原文。
2解题思路:
(1)对输入的一段欲编码的字符串进行统计各个字符出现的次数,并它们转化为权值{w1,w2,……,wN}构成n棵二叉树的集合F={T1,T2,……,Tn}把它们保存到结构体数组HT[n]中,其中{Ti是按它们的ASCⅡ码值先后排序。
其中每棵二叉树Ti中只有一个带权为Wi的根结点的权值为其左、右子树上根结点的权值之和。
(2)在HT[1..i]中选取两棵根结点的权值最小且没有被选过的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左、右子树上根结点的权值之和。
(3)哈夫曼树已经建立后,从叶子到根逆向求每一个字符的哈夫曼编码。
3.结构体定义
typedefstruct
{
floatweight;
unsignedintparent,lchild,rchild;
}HTNode,*HuffmanTree;
typedefchar**HuffmanCode;
4.代码
#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#include"conio.h"
#defineMAXSIZE60
/************************************定义赫夫曼树结构体*******************************************/
typedefstruct
{
floatweight;
unsignedintparent,lchild,rchild;
}HTNode,*HuffmanTree;
typedefchar**HuffmanCode;
/************************************************定义堆结构体**********************************************************/
typedefstruct
{
floatkey;//关键字项
intotherinfo;//其他数据项(此题目中用不到)
}RedType;
typedefstruct
{
RedTyper[MAXSIZE+1];//r[0]闲置用作哨兵
intlength;//顺序表长度
}SqList;
/**************************************************全局变量*************************************************************/
HuffmanTreeHT;//赫夫曼树
HuffmanCodeHC;//码值
FILE*fp,*fp1,*fp2;
inta[30]={0};
floatb[30];
float*w;//权
/****************************************测试解码(可以输入一个不正确的二进制码串)**************************************/
voidtestdecode()
{
charstr[200];//存放自己输入的码子
intp,p1,i;//解码的线索
charch;
printf("\n请根据以上各个字符的编码输入一串二进制码字(200个以内):
\n");
gets(str);
printf("以上码子解码后为:
\n");
p=59;//最初令p为树根整数,p由根顺着树枝一直到树叶
i=0;
ch=str[i++];
while(ch!
='\0')
{
for(;ch!
='\0'&&(HT[p].lchild!
=0||HT[p].rchild!
=0);)
{
if(ch=='0')
{
p=HT[p].lchild;
}
elseif(ch=='1')
{
p=HT[p].rchild;
}
else
{
printf("\n你输入了'0','1'之外的字符,无法正确译码,请检查!
\n");
return;//直接结束
}
ch=str[i++];//下一个码字不断的取下一个
}
if(p<=30)//小于等于30的时候才正确,有可能最后一位p还没有在1-30范围内的时候就没有二进制码了,也就是说二进制码最后不完整。
switch(p)
{
case27:
printf(",");
break;
case28:
printf(".");
break;
case29:
printf("");
break;
case30:
printf("\n");
break;
default:
printf("%c",p+96);
}
p1=p;//让p1记住p
p=59;//这里p要重置为59,因为经过上面的程序p已经变化了,不重置为1则HT[p].lchild!
=0||HT[p].rchild!
=0所以for语句无法进行!
!
!
!
}//while循环
if(p1>30)
printf("\n以上正确译出了前面的字符,由于你输入的二进制码不完整,最后一位字符无法译出,请检查!
\n");
}
/*******************************************下面是解码*********************************************************/
voiddecode()
{
intp;
charch;
printf("\n\n对码子解码后的如下:
\n");
fp1=fopen("bianma.txt","r");
if(!
fp1)
{
printf("cannotopenthisfile!
\n");
exit(0);
}
p=59;//最初令p为任意一个非零整数,p由根顺着树枝一直到树叶
ch=fgetc(fp1);
fp2=fopen("jiema.txt","w");
if(!
fp2)
{
printf("cannotopenthisfile!
\n");
exit(0);
}
while(ch!
=EOF)
{
for(;HT[p].lchild!
=0||HT[p].rchild!
=0;)
{
if(ch=='0')
{
p=HT[p].lchild;
}
else
{
p=HT[p].rchild;
}
ch=fgetc(fp1);//下一个码字不断的取下一个
}
switch(p)
{
case27:
printf(",");
fprintf(fp2,",");
break;
case28:
printf(".");
fprintf(fp2,".");
break;
case29:
printf("");
fprintf(fp2,"");
break;
case30:
printf("\n");
fprintf(fp2,"\n");
break;
default:
printf("%c",p+96);
fprintf(fp2,"%c",p+96);
}
p=59;//这里p要重置为59,因为经过上面的程序p已经变化了,不重置为1则HT[p].lchild!
=0||HT[p].rchild!
=0所以for语句无法进行!
!
!
!
}//while循环
printf("\n");
fprintf(fp2,"\n");
fclose(fp1);
fclose(fp2);
}
/*******************************************求最小权值*********************************************************/
voidminweight()
{
floatWeight=0;
inti;
for(i=0;i<30;i++)
Weight=Weight+strlen(HC[i+1])*b[i];
printf("最小权值是:
%f\n",Weight);
}
/*******************************************打印码子**********************************************************/
voidprintcode()
{
charch;
fp=fopen("Huffman.txt","r");
if(!
fp)
{
printf("cannotopenthisfile!
\n");
exit(0);
}
fp1=fopen("bianma.txt","w");
if(!
fp1)
{
printf("cannotopenthisfile!
\n");
exit(0);
}
printf("\n原英文文章经编码后如下:
\n");
ch=fgetc(fp);
while(ch!
=EOF)
{
if(ch>96&&ch<123)//小写字母
{
printf("%s",HC[ch-96]);
fputs(HC[ch-96],fp1);//输出到文件里面
ch=fgetc(fp);
continue;
}
if(ch>64&&ch<91)//大小字母
{
printf("%s",HC[ch-64]);
fputs(HC[ch-64],fp1);//输出到文件里面
ch=fgetc(fp);
continue;
}
if(ch==',')
{
printf("%s",HC[27]);
fputs(HC[27],fp1);
ch=fgetc(fp);
continue;
}
if(ch=='.')
{
printf("%s",HC[28]);
fputs(HC[28],fp1);
ch=fgetc(fp);
continue;
}
if(ch=='')
{
printf("%s",HC[29]);
fputs(HC[29],fp1);
ch=fgetc(fp);
continue;
}
if(ch=='\n')
{
printf("%s",HC[30]);
fputs(HC[30],fp1);
ch=fgetc(fp);
continue;
}
}
printf("\n\n");
fclose(fp);
fclose(fp1);
}
/*****************************************堆排序****************************************************/
voidHeapAdjust(SqList&L,ints,intm)
{
RedTyperc;
intj;
rc=L.r[s];
for(j=2*s;j<=m;j*=2)
{
if(jL.r[j+1].key)//即使等于也不要动,不用加1
++j;
if(rc.key<=L.r[j].key)//即使等于也不要动,直接跳出来
break;
L.r[s]=L.r[j];
s=j;
}
L.r[s]=rc;
}
voidselect(HuffmanTreet,inti,int&s1,int&s2)
{//此函数被调用一次则就用堆排序选择两个最小的赋给s1和s2
SqListL;
RedTyperc;
intj,k,n=1;
L.length=0;
for(j=1;j<=i;j++)
{
if(t[j].parent!
=0)
continue;
L.r[n].key=t[j].weight;//赋值好,用堆排序
L.r[n].otherinfo=j;//存放序号,j是一直在加一的,循环一次加1,但是n不是的只有在符合条件的情况下才加1
n++;
L.length++;//这样写很巧妙的
}
for(k=L.length/2;k>0;--k)
HeapAdjust(L,k,L.length);
s1=L.r[1].otherinfo;//最小的选出来了!
/***把最小的换到最下面***/
rc=L.r[1];
L.r[1]=L.r[L.length];//此次的select函数,进行堆排序的只有L.length个(parent!
=0的没有在里面),所以这里是L.length而不是i
L.r[L.length]=rc;
HeapAdjust(L,1,L.length-1);
s2=L.r[1].otherinfo;//次小的选出来了(这里当输入1428四个数时,第一次选出的s1=1,s2=3是对的,但第二次选出的s1=5,但s2是随机数)
}
/**************************************赫夫曼编码***************************************************/
voidHuffmanCoding(HuffmanTree&HT,HuffmanCode&HC,float*w,intn)//算法6.12
{
//w存放n个字符的权值(均>0),构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC
intm,i,s1,s2,start,k;
unsignedc,f;
HuffmanTreep;
char*cd;
if(n<=1)
return;
m=2*n-1;
w=(float*)malloc(30*sizeof(float));
for(i=0;i<30;i++)
*(w+i)=b[i];
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用
for(p=HT+1,i=1;i<=n;++i,++p,++w)
{
(*p).weight=*w;
(*p).parent=0;
(*p).lchild=0;
(*p).rchild=0;
}
for(i=n+1;i<=m;++i,++p)
(*p).parent=0;
for(i=n+1;i<=m;++i)//建赫夫曼树
{//在HT[1~i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2
select(HT,i-1,s1,s2);
HT[s1].parent=i;
HT[s2].parent=i;//最小的和次小的双亲已经不为0了,下次就不在它两中间找最小的和次小的了。
HT[i].lchild=s1;
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}//建立赫夫曼树也容易,关键是select这个子函数!
!
!
printf("赫夫曼树如下表:
\n");
printf("__________________________________________________________________\n");
printf("|_number__|__name__|___weight___|__parent__|__lchild__|__rchild__|\n");
for(k=1;k<=m;k++)
{
if(k<27)
printf("|___%2d____|__%c和%c__|__%f__|____%2d____|____%2d____|____%2d____|\n",k,k+64,k+96,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
if(k==27)
printf("|___%2d____|___,____|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
if(k==28)
printf("|___%2d____|___.____|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
if(k==29)
printf("|___%2d____|__空格__|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
if(k==30)
printf("|___%2d____|__换行__|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
if(k>30)
printf("|___%2d____|________|__%f__|____%2d____|____%2d____|____%2d____|\n",k,HT[k].weight,HT[k].parent,HT[k].lchild,HT[k].rchild);
}
printf("\n");
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
cd=(char*)malloc(n*sizeof(char));
cd[n-1]='\0';
for(i=1;i<=n;i++)
{
start=n-1;//这里f=HT[f].parent是很巧妙的!
!
!
for(c=i,f=HT[i].parent;f!
=0;c=f,f=HT[f].parent)//f=HT[i].parentf!
=0是结束条件,所有的编码最后都要回到HT[m],而只有HT[m]的parent是0!
!
!
//从叶子到根逆向求编码
if(HT[f].lchild==c)//c是左孩子则码值是0
cd[--start]='0';//这样逆着输,当我们正序输出的时候就恰好是想要的编码了!
!
!
else
cd[--start]='1';
HC[i]=(char*)malloc((n-start)*sizeof(char));
//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);//从cd复制编码(串)到HC,这里的&cd[start]是一个地址
}
free(cd);//释放工作空间
printf("经赫夫曼编码后码值如下:
\n");
for(i=1;i<=26;i++)
{
printf("%c和%c---->%f---->",i+64,i+96,HT[i].weight);
puts(HC[i]);
}
printf(",---->%f---->%s\n",HT[27].weight,HC[27]);
printf(".---->%f---->%s\n",HT[28].weight,HC[28]);
printf("空格---->%f---->%s\n",HT[29].weight,HC[29]);
printf("换行---->%f---->%s\