哈夫曼编译码器.docx
《哈夫曼编译码器.docx》由会员分享,可在线阅读,更多相关《哈夫曼编译码器.docx(21页珍藏版)》请在冰豆网上搜索。
哈夫曼编译码器
西安郵電大學
数据结构课程设计报告
题目:
哈夫曼编/译码器
院系名称:
专业名称:
班级:
学生姓名:
学号(8位):
指导教师:
设计起止时间:
一.设计目的
本次课程设计的主要目的是综合运用所学的数据结构知识,使用一种自己擅长的语言,解决问题,侧重对栈、哈夫曼树等相关内容的综合应用,使同学们能进一步熟悉掌握使用各种数据结构的实现,进一步提升自己分析问题的能力、培养编程能力、锻炼编程思维,进而不断提高同学们解决问题的能力,并为以后解决实际问题打下良好的基础。
二.设计内容
1.程序设计的环境为VisualC++,使用的语言为C语言。
2.设计了几种结构体:
哈夫曼树(字符、权值、左右孩子、标记位、编码),权值(字符、权值)。
3.使用的数据结构类型是二叉树。
4.读取已存的字符串信息:
与程序存放在同一目录下的souce.txt中。
5.创建哈夫曼树,并放入文件huffmantree.txt中。
6.编码操作:
通过折半查找得到字符的哈夫曼编码,并存放在code.txt文件中。
7.译码操作:
读取code.txt中的内容,译码,结果放入decode.txt文件中。
8.栈的基本操作:
栈的初始化、判空、出栈、入栈、取栈顶。
9.为使界面美观,还使用了清屏函数。
三.概要设计
1.功能模块图
对系统进行分析,给出系统结构图。
整个系统除了主函数外,另外还有14个函数,实现四项功能:
读取文件功能,编码功能,译码功能,写文件功能。
其基本功能模块如下图所示:
2.各个模块详细的功能描述。
(1).主函数main()
进入开始界面函数,读取字符串,编码,译码,最后到退出界面。
(2).界面功能Host()、Go_on()、Quit()
(3).读文件ReadFile()
读取与程序在同一目录下的souce.txt文件,获取字符串。
(4).统计字符串中各字符的权值CreateCount()
按照ASCII码的顺序将字符出现的次数记录。
(5).创建哈夫曼树CreateHT()
先创建叶子结点的各种信息,在经过两次查找最小权值而生成非叶子结点,并完善相关的信息。
(6).查找当前权值最小的字符的下标FindMin()
在未查找过的字符中查找。
(7).创建哈夫曼编码CreateHCode()
对哈夫曼进行先序遍历,为左孩子则为0,右孩子为1
(8).将创建好的哈夫曼树相关内容存放到文件中HTWriteFile()
将字符及其对应的编码存放在同一目录下的huffmantree.txt文件中。
(9).统计要编码的总长度GetHCodeLen()
各字符的权值和码长的总和。
(10).哈夫曼编码结果GetResultStr()
将要进行编码的字符串逐个按字符的编码进行编码。
(11).折半查找字符对应的编码HalfSearch()
根据字符的ASCII码的大小查找编码。
(12).写文件WriteFile()
将编码或译码结果分别存放在同一目录下的code.txt或decode.txt文件中。
(13).译码操作DeCode()
按照编码的0、1用先序遍历哈夫曼树,直到找到相对应的叶子,即字符的,追加入译码字符串中。
四.详细设计
1.功能函数的调用关系图
2.各功能函数的数据流程图
创建哈夫曼树:
创建哈夫曼编码:
译码操作:
编码操作:
3.重点设计及编码
//创建哈夫曼编码
voidCreateHCode(HUFFMAN_TREE*ht,introot,inti,char*s)
{
if(ht[root].leftchild==-1)//通过对树的先序遍历得到哈夫曼密码
{
s[i]='\0';
strcpy(ht[root].code,s);//遇到叶子结点停止此次遍历,并得到密码
}
else//若为左孩子则为0,为右孩子则为1
{
s[i]='0';
CreateHCode(ht,ht[root].leftchild,i+1,s);
s[i]='1';
CreateHCode(ht,ht[root].rightchild,i+1,s);
}
}
//折半查找要编码内容的相关信息
HUFFMAN_TREE*HalfSearch(HUFFMAN_TREE*ht,intHTLen,charc)
{
inthead=0,tail,middle,Find=FALSE;
tail=(HTLen+1)/2-1;
while(!
Find&&head<=tail)
{
middle=(head+tail)/2;
if(ht[middle].character==c)
Find=TRUE;
elseif(ht[middle].character>c)
tail=middle-1;
else
head=middle+1;
}
returnht+middle;
}
//译码操作
voidDeCode(HUFFMAN_TREE*ht,intHTLen,char*code,intcodelen)
{
char*str;
inti,j,root;//i是密码的下标,j是码串的下标、根结点
root=HTLen-1;
str=(char*)malloc(sizeof(char)*codelen);
for(i=j=0;i<=codelen;)
{//从根结点通过0,1查找左右孩子,遇叶子结点则回到根结点
if(ht[root].leftchild==-1)
{
str[j++]=ht[root].character;
root=HTLen-1;
}
else
root=code[i++]=='0'?
ht[root].leftchild:
ht[root].rightchild;
}
str[j]='\0';
WriteFile(str,"decode.txt");
printf("译码结果:
\n%s\n\n",str);
}
五.测试数据及运行结果
1.正常测试数据和运行结果
读取文件souce.txtx中的内容:
哈夫曼树的创建及编码:
译码操作:
2.异常测试数据及运行结果
读取文件失败:
六.调试情况,设计技巧及体会
1.改进方案
对自己设计进行评价,指出合理和不足之处,提出改进的方案。
通过第二周的编程,我完成了哈夫曼编码/译码器。
程序总体设计结构较合理,功能基本完善,界面友好清晰。
优点:
熟练的应用二叉树这个数据结构的基本操作。
使程序可以更高效的运行,实现对字符串进行哈夫曼编码、译码等基本功能。
也加深了自己对数据结构的认识。
缺点:
在某些方面存在着不足,比如,没有使用位运算来解决问题,使程序浪费了大量的存储空间;变换得到字符串的方式,如直接输入等,使程序更加灵活。
改进的方法:
使用位运算,对其进行压缩和解压缩。
2.体会
在为期两周的程序设计实习中,我受益颇多。
更进一步的了解和掌握了各种数据结构的操作。
熟练的使用数据结构以解决实际问题。
从中,我可以将书本知识应用于实践,为以后的计算机语言的学习打下了坚实的基础;同时,我分析问题的能力也在不断的提高,编程的能力也有显著的进步。
但在本次程序设计中,我学会的更多是坚持的精神和持之以恒的心态,哪怕报错的信息多到显示不完,也要静下心,认认真真一点一点地寻找错误的原因。
另外,我也学会了利用调试来查找出错点的方法,以及一些从未用过但是却功能特殊的函数。
同时真正意识到编写一个程序的不容易,以及多得考虑不完的问题,更是突显出团队合作的重要性。
七.参考文献
[1]耿国华.2008.《数据结构——C语言描述》.北京:
高等教育出版社
[2]王曙燕.2005.《C语言程序设计》.北京:
科学出版社
八.附录:
(可不要,不要打印源程序,只交电子版)
#include
#include
#include
#include
#defineTRUE1
#defineFALSE0
#defineN500
//哈夫曼树的定义
typedefstructHUFFMAN_TREE
{
charcharacter;//字符
intcount;//权值
intleftchild;//左孩子
intrightchild;//右孩子
intflag;//标记位,为0则表示还未查找过,1表示已查找过
char*code;//哈夫曼密码
}HUFFMAN_TREE;
//临时存储权值的定义
typedefstructCOUNT
{
charcharacter;
intcount;
}COUNT;
voidHost(void);
voidGo_on(void);
char*ReadFile(char*);
COUNT*CreateCount(char*,int*);
HUFFMAN_TREE*CreateHT(COUNT*,int);
intFindMin(HUFFMAN_TREE*,int);
voidCreateHCode(HUFFMAN_TREE*,int,int,char*);
voidHTWriteFile(HUFFMAN_TREE*,int);
intGetHCodeLen(HUFFMAN_TREE*,int,int);
char*GetResultStr(char*,HUFFMAN_TREE*,int,int);
HUFFMAN_TREE*HalfSearch(HUFFMAN_TREE*,int,char);
voidWriteFile(char*,char*);
voidDeCode(HUFFMAN_TREE*,int,char*,int);
voidQuit(void);
main()
{
char*str,*code,*result,*HTCode;//原串、编码串1、编码串2、编码串3
intHTLen,Leaf,codelen;//总结点数、叶子结点、密码长度
COUNT*count;//存放临时的所有权值
HUFFMAN_TREE*ht;//哈夫曼树
Host();
Go_on();
//读取文件操作
str=ReadFile("souce.txt");
printf("从文件souce.txt中读取的内容如下:
\n");
printf("%s\n\n",str);
//编码文件内容
Go_on();
count=CreateCount(str,&Leaf);
HTCode=(char*)malloc(sizeof(char)*Leaf);//得到编码的最大长度
HTLen=2*Leaf-1;//计算得到所有结点的个数
ht=CreateHT(count,HTLen);
CreateHCode(ht,HTLen-1,0,HTCode);
HTWriteFile(ht,Leaf);
codelen=GetHCodeLen(ht,HTLen,Leaf);
result=GetResultStr(str,ht,HTLen,codelen);
WriteFile(result,"code.txt");
free(result);
//进行译码的操作
Go_on();
code=ReadFile("code.txt");
printf("编码的内容如下:
\n%s\n\n",code);
DeCode(ht,HTLen,code,codelen);
free(code);
//销毁操作
free(ht);
free(count);
Go_on();
Quit();
}
voidQuit(void)
{
printf("\n\n---------------------------------谢谢使用!
-------------------------------------\n\n");
exit(0);
}
//译码操作
voidDeCode(HUFFMAN_TREE*ht,intHTLen,char*code,intcodelen)
{
char*str;
inti,j,root;//i是密码的下标,j是码串的下标、根结点
root=HTLen-1;
str=(char*)malloc(sizeof(char)*codelen);
for(i=j=0;i<=codelen;)//从根结点开始通过0,1值查找左,右孩子,遇叶子结点则重新回到根结点
{
if(ht[root].leftchild==-1)
{
str[j++]=ht[root].character;
root=HTLen-1;
}
else
root=code[i++]=='0'?
ht[root].leftchild:
ht[root].rightchild;
}
str[j]='\0';
WriteFile(str,"decode.txt");
printf("译码结果:
\n%s\n\n",str);
}
//写文件
voidWriteFile(char*result,char*fname)
{
FILE*fp;
fp=fopen(fname,"wt");
fprintf(fp,"%s",result);
printf("数据已存入%s文件中。
\n",fname);
fclose(fp);
}
//折半查找要编码内容的相关信息
HUFFMAN_TREE*HalfSearch(HUFFMAN_TREE*ht,intHTLen,charc)
{
inthead=0,tail,middle,Find=FALSE;
tail=(HTLen+1)/2-1;
while(!
Find&&head<=tail)
{
middle=(head+tail)/2;
if(ht[middle].character==c)
Find=TRUE;
elseif(ht[middle].character>c)
tail=middle-1;
else
head=middle+1;
}
returnht+middle;
}
//哈夫曼编码的结果
char*GetResultStr(char*str,HUFFMAN_TREE*ht,intHTLen,intcodelen)
{
char*result;//存放编码结果
inti;
result=(char*)malloc(sizeof(char)*codelen);
*result='\0';
for(i=0;str[i];i++)
strcat(result,HalfSearch(ht,HTLen,str[i])->code);
returnresult;
}
//计算要翻译的内容的哈夫曼密码的长度
intGetHCodeLen(HUFFMAN_TREE*ht,intHTLen,intLeaf)
{
inti,resLen=0;
for(i=0;iresLen+=ht[i].count*strlen(ht[i].code);
returnresLen+1;
}
//将创建好的哈弗曼数的字符和密码信息存入文件
voidHTWriteFile(HUFFMAN_TREE*ht,intLeaf)
{
inti;
FILE*fp;
fp=fopen("huffmantree.txt","wt");
for(i=0;ifprintf(fp,"%c\t%s\n",ht[i].character,ht[i].code);
printf("\n\n哈夫曼树创建成功,并已存入文件huffmantree.txt中。
\n");
fclose(fp);
}
//创建哈夫曼编码
voidCreateHCode(HUFFMAN_TREE*ht,introot,inti,char*s)
{
if(ht[root].leftchild==-1)//通过对树的先序遍历得到哈夫曼密码
{
s[i]='\0';
strcpy(ht[root].code,s);//遇到叶子结点停止此次遍历,并得到密码
}
else//若为左孩子则为0,为右孩子则为1
{
s[i]='0';
CreateHCode(ht,ht[root].leftchild,i+1,s);
s[i]='1';
CreateHCode(ht,ht[root].rightchild,i+1,s);
}
}
//查找当前权值最小的字符下标
intFindMin(HUFFMAN_TREE*ht,intlen)
{
inti,min;
for(i=0;ht[i].flag;i++)
;
for(min=i;iif(ht[i].flag==0&&ht[i].countmin=i;
ht[min].flag=1;
returnmin;
}
//创建哈夫曼树
HUFFMAN_TREE*CreateHT(COUNT*count,intHTLen)
{
HUFFMAN_TREE*ht=NULL;
inti,len,leftchild,rightchild;//下标、总结点、左孩子下标、右孩子下标
len=(HTLen+1)/2;//计算叶子结点数
ht=(HUFFMAN_TREE*)malloc(sizeof(HUFFMAN_TREE)*HTLen);
//初始化叶子结点
for(i=0;i{
ht[i].character=count[i].character;
ht[i].count=count[i].count;
ht[i].leftchild=ht[i].rightchild=-1;
ht[i].flag=0;
ht[i].code=(char*)malloc(sizeof(char)*len);//得到密码的最大的长度
}
//初始化非叶子结点
while(len{
leftchild=FindMin(ht,len);
rightchild=FindMin(ht,len);
ht[len].character='#';
ht[len].count=ht[leftchild].count+ht[rightchild].count;
ht[len].leftchild=leftchild;
ht[len].rightchild=rightchild;
ht[len].flag=0;
ht[len++].code=NULL;
}
returnht;
}
//创建临时的字符权值
COUNT*CreateCount(char*str,int*HTLen)
{
COUNT*frp=NULL;
intcount=0,total[128]={0},i,j;//出现字符的个数、统计
for(i=0;str[i];i++)
total[str[i]]++;
for(i=0;i<128;i++)
if(total[i])
count++;
frp=(COUNT*)malloc(sizeof(COUNT)*count);
for(i=j=0;i<128;i++)
if(total[i])
{
frp[j].character=i;
frp[j++].count=total[i];
}
*HTLen=count;//得到所有的叶子节点的数量
returnfrp;
}
char*ReadFile(char*fname)
{
FILE*fp;
charch,*str;
inti;
if((fp=fopen(fname,"rt"))==NULL)
{
printf("\n\n打开文件出错!
可能不存在这个文件!
\n\n");
exit(0);
}
str=(char*)malloc(sizeof(char)*N);
for(i=0;(ch=fgetc(fp))!
=EOF;i++)
str[i]=ch;
str[i]='\0';
fclose(fp);
printf("\n\n读取文件%s成功!
\n",fname);
returnstr;
}
voidGo_on(void)
{
printf("\n\n\n按任意键继续");
getch();
system("cls");
}
voidHost(void)
{
printf("\n\n---------------------欢迎使用哈夫曼编码、译码器---------------------------------\n\n");
printf("\t注意:
\n\t\t请将需要编码的数据提前存入同一目录下souce.txt文件中!
\n\n");
}