哈夫曼编译码器.docx
《哈夫曼编译码器.docx》由会员分享,可在线阅读,更多相关《哈夫曼编译码器.docx(20页珍藏版)》请在冰豆网上搜索。
![哈夫曼编译码器.docx](https://file1.bdocx.com/fileroot1/2023-2/4/39a4e70d-68f2-4204-adcb-ef66557ff204/39a4e70d-68f2-4204-adcb-ef66557ff2041.gif)
哈夫曼编译码器
数据结构课程设计
设计说明书
哈夫曼编/译器
起止日期:
2012年6月12日至2012年6月16日
学生姓名
李硕
班级
计本1103班
学号
11408100307
成绩
指导教师(签字)
计算机与通信学院
1.设计题目
哈夫曼编/译码器
2.运行环境
MicrosoftVisualC++6.0
3.需求分析
利用哈夫曼编码进行信息通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。
试为这样的信息传输写一个哈夫曼编/译码系统。
4.概要设计
1)抽象数据类型
ADTTriplet{
HuffmanTree();//哈夫曼树的构造函数
~HuffmanTree();//哈夫曼树的西沟函数
voidCreateHuffmanTree();//建造哈夫曼书函数
voidCreateHuffmanTreeFromKeyboard();//从键盘建立哈夫曼树函数
voidEncoder();//编码函数
voidDecoder();//译码函数
voidPrintHuffmanTree();//打印哈夫曼树
voidPrintHuffmanTree_aoru(intT,intlayer=1);//凹凸法打印曼树};
2)算法设计的思想
哈夫曼编码(HuffmanCoding)是一种编码方式,以哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。
是指使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。
这种方法是由David.A.Huffman发展起来的。
例如,在英文中,e的出现概率很高,而z的出现概率则最低。
当利用哈夫曼编码对一篇英文进行压缩时,e极有可能用一个位(bit)来表示,而z则可能花去25个位(不是26)。
用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个位。
二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。
倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。
3)程序总体及主要算法的流程图
哈夫曼编/译码器的程序流程图
(1)对输入的一段欲编码的字符串进行统计各个字符出现的次数,并它们转化为权值{w1,w2,……,wN}构成n棵二叉树的集合F={T1,T2,……,Tn}把它们保存到结构体数组HT[n]中,其中{Ti是按它们的ASCⅡ码值先后排序。
其中每棵二叉树Ti中只有一个带权为Wi的根结点的权值为其左、右子树上根结点的权值之和。
(2)在HT[1..i]中选取两棵根结点的权值最小且没有被选过的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左、右子树上根结点的权值之和。
(3)哈夫曼树已经建立后,从叶子到根逆向求每一个字符的哈夫曼编码。
(4)译码的过程是分解电文中字符串,从根出发,按字符'0',或'1'确定找左孩子或右孩子,直至叶子结点,便求的该子串相应字符并输出接着下一个字符。
5.用户说明书
进入程序运行界面后,按照要求输入。
其中“1”代表创建哈夫曼树,“2”代表显示创建的哈夫曼树,“3”代表编码,“4”代表译码,“5”代表退出。
按顺序选择12345,即可实现哈夫曼树的编/译码。
6.源代码
#include
#include
#include
typedefstruct{
intweight;
charch;
intparent,lchild,rchild;
}HTNode,*HuffmanTree;
typedefchar**HuffmanCode;//指向赫夫曼编码的指针
voidwelcome();//打印操作选择界面
voidHuffmanCoding(HuffmanTree&,char*,int*,int);//建立赫夫曼树的算法
voidselect(HuffmanTreeHT,intj,int*s1,int*s2);//从目前已建好的赫夫曼树中选择parent为0且weight最小的两个结点
voidInit();//输入n个字符及其对应的权值,根据权值建立哈夫曼树
voidCoding();//编码
voidDecoding();//译码
voidPrint_code();//打印译码好的代码文件
voidPrint_tree();//以凹凸表形式打印哈夫曼树
intRead_tree(HuffmanTree&);//从文件中读入赫夫曼树
voidfind(HuffmanTree&HT,char*code,char*text,inti,intm);//译码时根据01字符串寻找相应叶子节点的递归算法
voidConvert_tree(unsignedcharT[100][100],ints,int*i,intj);//将内存中的赫夫曼树转换成凹凸表形式的赫夫曼树
HuffmanTreeHT;//全局变量,指向存放赫夫曼树的存储空间
intn=0;//全局变量,存放赫夫曼树叶子结点的数目
intmain()
{
charselect;
while
(1)
{
welcome();
scanf("%c",&select);
switch(select)
{
case'i':
case'I':
Init();break;
case'c':
case'C':
Coding();break;
case'd':
case'D':
Decoding();break;
case'p':
case'P':
Print_code();break;
case't':
case'T':
Print_tree();break;
case'e':
case'E':
exit
(1);
default:
printf("Inputerror!
\n");
}
getchar();
}
return0;
}
voidwelcome()//打印操作选择界面
{
printf("┏━━━━━━━━━━━━━━━━━━━━━━┓\n");
printf("┃欢迎使用哈夫曼编/译码器┃\n");
printf("┃━━━━━━━━━━━━━━━━━━━━━━┃\n");
printf("┃请选择功能:
┃\n");
printf("┃I━━建立一个哈夫曼树.┃\n");
printf("┃C━━编译一段字符串.┃\n");
printf("┃D━━解码一段字符串.┃\n");
printf("┃P━━打印出编码的文件.┃\n");
printf("┃T━━打印出这个哈夫曼树.┃\n");
printf("┃E━━退出此编译器.┃\n");
printf("┃【李硕制作】┃\n");
printf("┗━━━━━━━━━━━━━━━━━━━━━━┛\n");
}
//////////////////////////////////////////////////////////////////////////////////////
/*初始化函数,输入n个字符及其对应的权值,根据权值建立哈夫曼树,并将其存于文件hfmtree中*/
voidInit()
{
FILE*fp;
charb;
inti,n,w[52];//w数组存放n个字符的权值
charcharacter[52];//存放n个字符
printf("\n输入字符个数n:
");
scanf("%d",&n);//输入字符集大小
printf("输入%d个字符及其对应的权值:
\n",n);
for(i=0;i{
b=getchar();
scanf("%c",&character[i]);
scanf("%d",&w[i]);//输入n个字符和对应的权值
}
HuffmanCoding(HT,character,w,n);//建立赫夫曼树
if((fp=fopen("hfmtree.txt","w"))==NULL)
printf("Openfilehfmtree.txterror!
\n");
for(i=1;i<=2*n-1;i++)
{
if(fwrite(&HT[i],sizeof(HTNode),1,fp)!
=1)//将建立的赫夫曼树存入文件hfmtree.txt中
printf("Filewriteerror!
\n");
}
printf("\n建立赫夫曼树成功,已将其存于文件hfmtree.txt中\n");
fclose(fp);
}
///////////////////////////////////////////////////////////////////////////////////
//////建立赫夫曼树的算法///////////////////////////////////////////////////////////
voidHuffmanCoding(HuffmanTree&HT,char*character,int*w,intn)
{
intm,i,s1,s2;
HuffmanTreep;
if(n<=1)return;
m=2*n-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
for(p=HT+1,i=1;i<=n;++i,++p,++character,++w)
{
p->ch=*character;
p->weight=*w;
p->parent=0;
p->lchild=0;
p->rchild=0;
}
for(;i<=m;++i,++p)
{
p->ch=0;
p->weight=0;
p->parent=0;
p->lchild=0;
p->rchild=0;
}
for(i=n+1;i<=m;++i)
{
select(HT,i-1,&s1,&s2);
HT[s1].parent=i;
HT[s2].parent=i;
HT[i].lchild=s1;
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
}
///////////////////////////////////////////////////////////////////////////////
/*从HT[1]到HT[j]中选择parent为0且weight最小的两个结点,用s1和s2返回其序号*/
voidselect(HuffmanTreeHT,intj,int*s1,int*s2)
{
inti;//找weight最小的结点
for(i=1;i<=j;i++)
if(HT[i].parent==0)
{
*s1=i;
break;
}
for(;i<=j;i++)
if((HT[i].parent==0)&&(HT[i].weight*s1=i;
HT[*s1].parent=1;
//找weight次小的结点
for(i=1;i<=j;i++)
if(HT[i].parent==0)
{
*s2=i;
break;
}
for(;i<=j;i++)
if((HT[i].parent==0)&&(i!
=*s1)&&(HT[i].weight*s2=i;
}
///////////////////////////////////////////////////////////////////////////////
/*对文件tobetrans中的正文进行编码,然后将结果存入文件codefile中*/
voidCoding()
{
FILE*fp,*fw;
inti,f,c,start;
char*cd;
HuffmanCodeHC;
if(n==0)
n=Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
/////以下程序段求赫夫曼树中各叶子节点的字符对应的的编码,并存于HC指向的空间中
{
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;
for(c=i,f=HT[i].parent;f!
=0;c=f,f=HT[f].parent)
if(HT[f].lchild==c)
cd[--start]='0';
elsecd[--start]='1';
HC[i]=(char*)malloc((n-start)*sizeof(char));
strcpy(HC[i],&cd[start]);
}
free(cd);
}
/////////////////////////////////////////////////////////////////////////////////////
if((fp=fopen("tobetrans.txt","rb"))==NULL)
printf("Openfiletobetrans.txterror!
\n");
if((fw=fopen("codefile.txt","wb+"))==NULL)
printf("Openfilecodefile.txterror!
\n");
chartemp;
fscanf(fp,"%c",&temp);//从文件读入第一个字符
while(!
feof(fp))
{
for(i=1;i<=n;i++)
if(HT[i].ch==temp)
break;//在赫夫曼树中查找字符所在的位置
for(intr=0;HC[i][r]!
='\0';r++)//将字符对应的编码存入文件
fputc(HC[i][r],fw);
fscanf(fp,"%c",&temp);//从文件读入下一个字符
}
fclose(fw);
fclose(fp);
printf("\n对文件hfmtree.txt编码成功,结果已存入codefile.txt中。
\n\n");
}
/////////////////////////////////////////////////////////////////////////
/*将文件codefile中的代码进行译码,结果存入文件textfile中*/
voidDecoding()
{
FILE*fp,*fw;
intm,i;
char*code,*text,*p;
if(n==0)
n=Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
if((fp=fopen("codefile.txt","rb"))==NULL)
printf("Openfilecodefile.txterror!
\n");
if((fw=fopen("textfile.txt","wb+"))==NULL)
printf("Openfiletextfile.txterror!
\n");
code=(char*)malloc(sizeof(char));
fscanf(fp,"%c",code);//从文件读入一个字符
for(i=1;!
feof(fp);i++)
{
code=(char*)realloc(code,(i+1)*sizeof(char));//增加空间
fscanf(fp,"%c",&code[i]);//从文件读入下一个字符
}
code[i-1]='\0';
/////////到此codefile.txt文件中的字符已全部读入,存放在code数组中
text=(char*)malloc(100*sizeof(char));
p=text;
m=2*n-1;
if(*code=='0')
find(HT,code,text,HT[m].lchild,m);//从根节点的左子树去找
else
find(HT,code,text,HT[m].rchild,m);//从根节点的右子树去找
for(i=0;p[i]!
='\0';i++)//把译码好的字符存入文件textfile.txt中
fputc(p[i],fw);
fclose(fp);
fclose(fw);
printf("\n对codefile.txt文件译码成功,结果已存入textfile.txt文件。
\n\n");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
/*将文件codefi1e以紧凑格式显示在终端上,每行50个代码。
同时将此字符形式的编码文件写入文件codeprint中。
*/
voidPrint_code()
{
FILE*fp,*fw;
chartemp;
inti;
if((fp=fopen("codefile.txt","rb"))==NULL)
printf("Openfilecodefile.txterror!
\n");
if((fw=fopen("codeprint.txt","wb+"))==NULL)
printf("Openfilecodeprint.txterror!
\n");
printf("\n文件codefi1e以紧凑格式显示如下:
\n");
fscanf(fp,"%c",&temp);//从文件读入一个字符
for(i=1;!
feof(fp);i++)
{
printf("%c",temp);
if(i%50==0)printf("\n");
fputc(temp,fw);//将该字符存入文件codeprint.txt中
fscanf(fp,"%c",&temp);//从文件读入一个字符
}
printf("\n\n此字符形式的编码已写入文件codeprint.txt中.\n\n");
fclose(fp);
fclose(fw);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
/*将已在内存中的哈夫曼树以凹凸表形式显示在屏幕上,同时将此字符形式的哈夫曼树写入文件treeprint中。
*/
voidPrint_tree()
{
unsignedcharT[100][100];
inti,j,m=0;
FILE*fp;
if(n==0)
n=Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
Convert_tree(T,0,&m,2*n-1);//将内存中的赫夫曼树转换成凹凸表形式的树,存于数组T中
if((fp=fopen("treeprint.txt","wb+"))==NULL)
printf("Openfiletreeprint.txterror!
\n");
printf("\n以凹凸表形式打印已建好的赫夫曼树:
\n");
for(i=1;i<=2*n-1;i++)
{
for(j=0;T[i][j]!
=0;j++)
{
if(T[i][j]=='')
{
printf("");
fputc(T[i][j],fp);
}
else
{
printf("%d",T[i][j]);
fprintf(fp,"%d\n",T[i][j]);
}
}
printf("\n");
}
fclose(fp);
printf("\n此字符形式的哈夫曼树已写入文件treeprint.txt中.\n\n");
}
//////////////////////////////////////////////////////////////////////////////////
/*从文件hfmtree.txt中读入赫夫曼树,返回叶子节点数*/
intRead_tree(HuffmanTree&HT)
{
FILE*fp;
inti,n;
HT=(HuffmanTree)malloc(sizeof(HTNode));
if((fp=fopen("hfmtree.txt","r"))==NULL)
printf("Openfilehfmtree.txterror!
\n");
for(i=1;!
feof(fp);i++)
{
HT=(HuffmanTree)realloc(HT,(i+1)*sizeof(HTNode));//增加空间
fread(&HT[i],sizeof(HTNode),1,fp);//读入一个节点信息
}
fclose(fp);
n=(i-1)/2;
returnn;
}
////////////////////////////////////////////////////////////////
/*译码时根据01字符串寻找相应叶子节点的递归算法*/
voidfind(HuffmanTree&HT,char*code,char*text,inti,intm)
{
if(*code!
='\0')//若译码未结束
{
code++;
if(HT[i].l