霍夫曼编译码器数据结构课程设计样本.docx
《霍夫曼编译码器数据结构课程设计样本.docx》由会员分享,可在线阅读,更多相关《霍夫曼编译码器数据结构课程设计样本.docx(23页珍藏版)》请在冰点文库上搜索。
![霍夫曼编译码器数据结构课程设计样本.docx](https://file1.bingdoc.com/fileroot1/2023-5/24/02400480-92d1-4ddf-abb1-a58c60ebae01/02400480-92d1-4ddf-abb1-a58c60ebae011.gif)
霍夫曼编译码器数据结构课程设计样本
课程设计报告
课程设计名称:
数据结构课程设计
课程设计题目:
霍夫曼编/译码器
院(系):
专业:
班级:
学号:
姓名:
指导教师:
目录
1需求分析1
1.1问题描述1
1.2问题理解1
2系统设计2
2.1总体方案设计2
2.2数据结构设计2
2.3函数设计3
2.4关键流程4
2.4.1系统主流程4
2.4.3创建霍夫曼树函数流程函数7
2.4.4霍夫曼树建立霍夫曼表的函数流程8
3调试分析10
4测试及运行结果11
参考文献13
附录14
1需求分析
1.1问题描述
设计一个利用霍夫曼算法的编码与译码系统,可以接收来自键盘输入的字符集大小、字符和权值信息,创建霍夫曼树生成霍夫曼编码并能对其进行译码的系统。
1.2问题理解
首先,建立的霍夫曼编码与译码器并能够接受字符集大小、字符和权值信息,因此用字符数组存储字符串,把相同字符出现的次数作为每个字符的权值,用数组存储,建立一个链表,依次存储字符串中的字符,建立霍夫曼树,选取两个权值最小的字符组合,依次构成霍夫曼树,以左孩子码为0,右孩子为1,对霍夫曼树中的节点进行编码以及译码的应用。
2系统设计
2.1总体方案设计
打开存放字符或编码的文件,将文件中的字符串存入字符串数组里,查找字符串中字符的个数和每个字符出现的次数,创建霍夫曼树,将各个字符出现的次数作为权值,存入霍夫曼链表的前n个单元中,将后n-1个节点赋权值,建树,从每个叶子节点开始,利用霍夫曼树对每个字符进行编码,最终建立一个霍夫曼表,利用霍夫曼编码表对整个字符串进行编码,对霍夫曼编码进行解码,放入字符串s中。
2.2数据结构设计
本程序的数据结构设计用了霍夫曼树的节点结构体,编码结构体其结构体如下:
typedefstructnode
{
intweight;
structnode*LChild,*RChild,*Parent;
structnode*next;
}HFMNode,*HFMTree;
typedefstruct
{
charch;
charcode[N+1];
intstart;
}CodeNode;
2.3函数设计
﹙1﹚本系统所设计的函数见表2.1。
表2.1函数列表
函数名称
函数原型
功能描述
main
voidmain()
进入编码程序
TransCode
TransCode(charcode[],charstr[],charss[],HFMTree*HT,CodeNodeHC[])
对经过编码得到的字符串码进行解码
Coding
Coding(chars[],charstr[],charcode[],intcount[],HFMTree*HT,CodeNodeHC[])
对文件中的字符串进行编码
DeCoding
DeCoding(charcode[],HFMTreeHT,charstr[],chars[])
将编码进行解码存入字符串数组ss[]中
TotalCoding
TotalCoding(chars[],CodeNodeHC[],charcode[])
利用哈夫曼编码表对整个字符串进行编码
HFMCode
HFMCode(HFMTreeHT,CodeNodeHC[],charstr[])
/从每个叶子节点开始,利用哈夫曼树对每个字符进行编码,最终建立一个哈夫曼表
CreatHFMTree
CreatHFMTree(HFMTree*HT,intcount[])
创建哈夫曼树
SelectMin
SelectMin(HFMTreeHT,intk,HFMTree*HT1,HFMTree*HT2)
查找哈夫曼链表中两个权值最小的节点
SearchStr
SearchStr(chars[],charstr[],intcount[])
查找字符串中字符的个数和每个字符出现的次数
Save
Save(chars[])
保存字符或编码到文件中
Open
Open(chars[])
打开存放字符或编码的文件,将其存入字符串数组中
﹙2﹚本系统函数的调用关系见图2.1。
图2.1函数调用关系
2.4关键流程
2.4.1系统主流程
(1)主函数的简单描述:
首先主函数调用清屏函数Clearscreen(),然后,当choice='1,调用函数Coding(s,str,code,count,&HT,HC),实现对文件中的字符串进行编码功能;当choice='2,调用函数TransCode(code,str,ss,&HT,HC),实现对经过编码得到的字符串码进行解码功能;当choice!
='0,主函数进行循环;当choice='0,主函数调用结束。
(2)主函数的流程图
本函数的具体流程见图2.2。
s[M]和ss[M]是两个字符数组;Clearscreen()是清屏函数;choice则是switch语句;Coding是对文件中的字符串进行编码;TransCode是对经过编码得到的字符串码进行解码。
图2.2主函数的流程图
2.4.2打开存放字符文件,存入字符串数组流程
(1)打开存放字符文件,存入字符串数组流程的简单描述:
本函数的功能是:
首先判断文件是否为空,然后将文件中的字符存到数组中,当数组中的元素不为EOF时就进行循环直到不满足循环条件结束。
(2)打开存放字符文件,存入字符串数组流程流程图。
本函数的具体流程见图2.3。
fp是一个文件;s[i]存储文件中字符fget(fp)。
图2.3打开存放字符文件,存入字符串数组流程图
2.4.3创建霍夫曼树函数流程函数
(1)霍夫曼树函数的简单描述:
写出本函数的具体功能为:
当满足i<2*n-1时,创建哈夫曼链表,然后将字符出现的次数作为权值钱n个链表结点中,最后对后n-1个接点赋值建树。
(2)霍夫曼树函数的流程图。
本函数的具体流程见图2.4。
i是计数量;p是创建的HFMTree结构体;HT是树根;p->weight是权值;count起计数作用;SelectMin查找字符串中字符的个数和每个字符出现的次数。
图2.4霍夫曼树函数的流程图
2.4.4霍夫曼树建立霍夫曼表的函数流程
(1)霍夫曼树对每个字符进行编码,最终建立一个霍夫曼表的函数简单描述:
本函数的功能是:
首先将字符存入哈夫曼编码结构体数组的字符单元中,然后将叶子结点的数值转换到创建的霍夫曼表中。
(2)霍夫曼树建立霍夫曼表的函数流程图
本函数的具体流程见图2.5。
HC[]是CodeNode结构体数组,str[]是字符型数组;q->Parent不为空的作用是判断q所指向的节点,左孩子置0,右孩子置1q==q->Parent->LChild是将p指向p的左孩子;p=p->next是将将p指向p的下一个结构体。
图2.5霍夫曼树建立霍夫曼表的函数流程图
3调试分析
(1)问题1
●问题描述:
编码过程中字符串编码总是缺少很多位,甚至不能编码一些字符。
●问题分析:
字符串编码总是缺少很多位,说明导致这种情况的可能是节点不够多,致使编码没空间存储。
●解决方法:
初始化霍夫曼链表的节点不够,如果假设字符串有n个,那链表节点至少得有2n-1个,比如对4个字符串编码,先在4个字符里选2个权值最小的字符,2个字符权值相加,生成新的一个节点存储,现在还剩3个节点,同理3选2,生成新节点,剩余一个节点最后与上一步生成的节点配对,因此共需要生成3个新节点来存储。
(2)问题2
●问题描述:
程序运行中,当创建树时,无法将树的元素存入到其中。
●问题分析:
无法存入其中,可能有两方面原因,第一:
没有初始化树,即没有申请空间;第二方面:
可能是树的结构体定义出现错误。
●解决方法:
在树的创建中,首先对其进行初始化,申请存储空间,然后再输入树的元素,经过调试,结果正确。
4测试及运行结果
(1)编码的具体的测试结果如图4.1所示。
图4.1编码的测试结果
(2)编码和保存编码的具体的测试结果如图4.2所示。
图4.2编码和保存编码的测试结果
(3)译码和保存译码的具体的测试结果如图4.3所示。
图4.3译码和保存译码的测试结果
参考文献
[1]严蔚敏,吴伟民.数据结构(C语言版)[M].北京:
清华大学出版社,2007
[2]吕国英.算法设计与分析[M].北京:
清华大学出版社,2006
[3]徐宝文,李志.C程序设计语言[M].北京:
机械工业出版社,2004
[4]ErichGamma,RichardHelm.设计模式(英文版)[M].北京:
机械工业出版社,2004
附录
源程序清单:
#include
#include
#include
#defineM10000
#defineN128
typedefstructnode
{
intweight;
structnode*LChild,*RChild,*Parent;
structnode*next;//指向建立的哈夫曼树的下一个节点
}HFMNode,*HFMTree;
typedefstruct
{
charch;
charcode[N+1];
intstart;
}CodeNode;
intn;
voidclearscreen()
{
system("cls");
}
voidSave(chars[])
{
charname[10];
FILE*fp;
printf("请输入要保存的文件名:
");
gets(name);
if((fp=fopen(name,"wt"))==NULL)
{
printf("存储失败!
");
exit
(1);
}
fputs(s,fp);
printf("\n保存成功,文件名为:
%s。
\n",name);
printf("\n按回车键继续...");
getchar();
fclose(fp);
}
voidOpen(chars[])
{
charname[10];
FILE*fp;
inti=0;
printf("请输入要打开的文件名:
");
gets(name);
if((fp=fopen(name,"rt"))==NULL)
{
printf("打开失败!
\n");
exit
(1);
}
s[i++]=fgetc(fp);
while(s[i-1]!
=EOF)
s[i++]=fgetc(fp);
s[i]='\0';
fclose(fp);
}
voidSearchStr(chars[],charstr[],intcount[])
{
inti,j,k=0;
for(i=0;icount[i]=0;
for(i=0;s[i];i++)
{
for(j=0;jif(str[j]==s[i])
{
count[j]++;
break;
}
if(j==k)
{
str[k]=s[i];
count[k++]++;
}
}
str[k]='\0';
n=k-1;//将实际的字符个数作为叶子节点个数存入n
}
voidSelectMin(HFMTreeHT,intk,HFMTree*HT1,HFMTree*HT2)
{
inti,min;
HFMTreep;
min=32767;
for(i=0,p=HT;inext)
if(p->weightParent==0)
{
min=p->weight;
*HT1=p;
}
min=32767;
for(i=0,p=HT;inext)
if(p->weightParent==0&&p!
=*HT1)
{
min=p->weight;
*HT2=p;
}
}
voidCreatHFMTree(HFMTree*HT,intcount[])
{
inti;
HFMTreep,HT1,HT2;
p=*HT=(HFMTree)malloc(sizeof(HFMNode));
p->next=p->LChild=p->RChild=p->Parent=NULL;
for(i=1;i<2*n-1;i++)
{
p->next=(HFMTree)malloc(sizeof(HFMNode));
p=p->next;
p->next=p->LChild=p->RChild=p->Parent=NULL;
}
for(i=0,p=*HT;i{
p->weight=count[i];
p=p->next;
}
for(i=n;i<2*n-1;i++)
{
SelectMin(*HT,i,&HT1,&HT2);
HT1->Parent=HT2->Parent=p;
p->LChild=HT1;
p->RChild=HT2;
p->weight=HT1->weight+HT2->weight;
p=p->next;
}
}
voidHFMCode(HFMTreeHT,CodeNodeHC[],charstr[])
{
inti;
intj;
HFMTreeq,p=HT;
for(i=0;i{
HC[i].ch=str[i];
HC[i].code[n-1]='\0';
}
for(i=0;i{
HC[i].start=n-1;
for(q=p;q->Parent;q=q->Parent)
if(q==q->Parent->LChild)
HC[i].code[--HC[i].start]='0';
elseHC[i].code[--HC[i].start]='1';
p=p->next;
}
}
voidTotalCoding(chars[],CodeNodeHC[],charcode[])
{
inti,j;
code[0]='\0';
for(i=0;s[i];i++)
for(j=0;jif(s[i]==HC[j].ch)
strcpy(code+strlen(code),HC[j].code+HC[j].start);
}
voidDeCoding(charcode[],HFMTreeHT,charstr[],chars[])
{
inti,j,k=0;
HFMTreeroot,p,q;
for(root=HT;root->Parent;root=root->Parent);
for(i=0,p=root;code[i];i++)
{
if(code[i]=='0')
p=p->LChild;
elsep=p->RChild;
if(p->LChild==NULL&&p->RChild==NULL)
{
for(j=0,q=HT;q!
=p;q=q->next,j++);
s[k++]=str[j];
p=root;
}
}
s[k]='\0';
}
voidCoding(chars[],charstr[],charcode[],intcount[],HFMTree*HT,CodeNodeHC[])
{
clearscreen();
printf("\n打开存放字符串的文件...\n\n");
Open(s);
SearchStr(s,str,count);
CreatHFMTree(HT,count);
HFMCode(*HT,HC,str);
TotalCoding(s,HC,code);
printf("\n读入的字符串为:
\n");
puts(s);
printf("\n最终的哈夫曼编码是:
\n");
puts(code);
printf("\n保存编码,");
Save(code);
}
voidTransCode(charcode[],charstr[],charss[],HFMTree*HT,CodeNodeHC[])
{
clearscreen();
printf("\n打开编码的文件...\n\n");
Open(code);
DeCoding(code,*HT,str,ss);
printf("\n最终得到的字符串为:
\n");
puts(ss);
printf("\n保存译码,");
Save(ss);
}
main()
{
chars[M],ss[M];
charstr[N];
intcount[N];
charcode[M];
charchoice;
HFMTreeHT;
CodeNodeHC[N];
do
{
clearscreen();
printf("\n\n");
printf("************哈夫曼树************\n");
printf("*************\n");
printf("******1.编码。
*****\n");
printf("*****2.译码。
****\n");
printf("****0.退出。
***\n");
printf("*********\n");
printf("***********\n");
printf("*************\n");
printf("**请输入相应操作的序号(0-2)**\n");
printf("********************************\n");
scanf("%c",&choice);
getchar();
switch(choice)
{
case'1':
Coding(s,str,code,count,&HT,HC);break;
case'2':
TransCode(code,str,ss,&HT,HC);break;
case'0':
break;
default:
printf("输入错误!
请输入正确信息!
\n");
}
}while(choice!
='0');
}
课程设计总结:
通过这次的课程设计,深了我对程序的理解,特别是对一个大程序,来怎样进行对他组织及编写,数据结构这学期已经学完了。
到了这最后的阶段,有了这样一个课程设计,感觉是挺好的,能够加强对一个大程序的宏观概念,使我更深刻的了解到学好《数据结构》这门课的重要性。
在学习《数据结构》课程时,链表与树是我学习的难点与课程的重点,通过这次的课程设计,使我更清楚的明白链表与树深刻的含义,更加熟悉树的创建与链表的遍历并充分掌握其相关的内容。
这个程序是通过调用多个子函数来把它实现,通过运用双亲孩子法创建霍夫曼树,建立霍夫曼树编码表来进行对一串字符串编码与译码。
在整个课程设计的过程中,我慢慢了解到计算机科学与技术专业要求我们要有旺盛的求知欲,我们平时所学的是理论基础,但是对于计算机专业的人来说,光有理论是不够的,必须具备实践能力,通过这次课程设计使我们增强了实践能力,并能够与理论知识相结合,对今后的学习与这方面的发展奠定了基础。
总的来说,这次的课程设计,充分锻炼了自己的动手能力及学到一些课程内其他没有学到过的知识,感觉获益良多!
感谢老师的耐心指导与帮助,在您的耐心指导下使我对课设的实践更得心应手了!
下总之,课程设计是对我们学习的一种检验与提高,具有一定的挑战性,我期望我的未来充满这种挑战。
指导教师评语:
指导教师(签字):
年月日
课程设计成绩