哈夫曼编译器.docx
《哈夫曼编译器.docx》由会员分享,可在线阅读,更多相关《哈夫曼编译器.docx(22页珍藏版)》请在冰豆网上搜索。
![哈夫曼编译器.docx](https://file1.bdocx.com/fileroot1/2022-12/30/6552146f-841b-484f-b973-51feca8992ed/6552146f-841b-484f-b973-51feca8992ed1.gif)
哈夫曼编译器
沈阳航空航天大学
课程设计报告
课程设计名称:
数据结构课程设计
课程设计题目:
哈夫曼编码和译码器
院(系):
计算机学院
专业:
计算机科学与技术
班级:
24010101
学号:
2012040101034
姓名:
赵文焕
指导教师:
许清
1.题目分析
1.1.需求概述
本次课程设计的目标是实现一个哈夫曼编码和译码器。
该哈夫曼编码和译码器需要根据用户输入的字符集及相应字符出现的频率,对字符集所包含的字符进行哈夫曼编码。
同时,作为编码器需要其对用户提供的明文字符串进行编码,使明文字符串变为二进制密文;作为译码器需要对用户提供的二进制密文进行译码,使二进制密文变为字符明文。
1.2.系统功能需求分析
通过对课程设计的题目分析,可以得出哈夫曼编码和译码器的功能需求,需求如下:
1)读取用户输入的字符集和相应字符出现的频率;
2)根据用户输入构建哈夫曼树;
3)根据哈夫曼树构建字符-哈夫曼编码对照表;
4)根据字符-哈夫曼编码对照表对明文字符串进行编码;
5)根据哈夫曼树对二进制密文进行译码。
1.
2.程序设计
2.1.系统功能模块说明
根据对系统的分析,哈夫曼编码与译码器系统共分为五个功能模块,分别为:
用户输入获取模块、哈夫曼树构造模块、字符-哈夫曼编码对照表构造模块、编码模块、译码模块。
2.1.1.系统功能模块结构
自底向上考虑各系统功能模块之间的依赖关系,译码模块依赖于哈夫曼树构造模块,编码模块依赖于字符-哈夫曼编码对照表构造模块,字符-哈夫曼编码对照表构造模块依赖于哈夫曼编码构造模块,哈夫曼编码构造模块依赖于用户输入获取模块。
系统功能结构框图如图2-1:
图21哈夫曼编码与译码器系统功能结构框图
1.
2.
2.1.
2.1.1.
2.1.2.系统模块功能说明
1)用户输入获取模块
获取并保存用户从键盘上输入的字符集和相应字符出现的频率。
2)哈夫曼树构造模块
根据用户输入获取模块保存的字符数据,构造哈夫曼树。
3)字符-哈夫曼编码对照表构造模块
根据哈夫曼树构造模块构造的哈夫曼树,建立字符-哈夫曼编码对照表。
4)编码模块
根据字符-哈夫曼编码对照表构造模块构造的字符-哈夫曼编码对照表,对用户提供的明文进行编码。
5)译码模块
根据哈夫曼树构造模块构造的哈夫曼树,对用户提供的密文字符进行译码。
2.2.数据结构说明
在程序中主要用到了二叉树和链表等数据结构。
2.2.1.结构体定义说明
1)struct_NODE结构
结构体定义如下:
typedefstruct_NODE{
charword;
intvalue;
_NODE*left,*right;
}Node,*LPNode;
结构体用途:
作为哈夫曼树的结点结构,构成哈夫曼树。
2)struct_CONTAINER结构
结构体定义如下:
typedefstruct_CONTAINER{
LPNodev;
struct_CONTAINER*last,*next;
}Container,*LPContainer;
结构体用途:
用于在用户输入时保存字符信息,并构成双向链表。
3)struct_CODENODE结构
结构体定义如下:
typedefstruct_CODENODE{
charword;
charcode[100];
struct_CODENODE*next;
}CodeNode,*LPCodeNode;
结构体用途:
作为单链表的结点结构,构成字符-哈夫曼编码对照表。
2.2.2.哈夫曼树
在本程序中,哈夫曼树是使用struct_NODE结构构建的二叉树,其满足树的叶子结点的带全路径和在所有可能组成的二叉树中最小。
2.2.3.字符-哈夫曼编码对照表
在本程序中,字符-哈夫曼编码对照表是一个单链表,用于保存字符与哈夫曼编码的对应关系。
2.3.函数说明
1)GetInput函数
函数声明:
LPNodeGetInput();
参数声明:
无参数。
返回值说明:
返回创建的哈夫曼树指针。
函数功能:
该函数的功能是读取用户输入的字符集数据,并构建相应的哈夫曼树。
函数的返回值是哈夫曼树的指针。
2)createHuffmanTree函数
函数声明:
LPNodecreateHuffmanTree(LPContainerlist);
参数说明:
list:
用来构建哈夫曼树的数据链表。
返回值说明:
返回构建的哈夫曼树。
函数功能:
该函数的功能是根据用户输入构建哈夫曼树。
3)createCodeList函数
函数声明:
LPCodeNodecreateCodeList(LPNodetree);
参数说明:
tree:
哈夫曼树。
返回值说明:
返回字符-哈夫曼编码对照表。
函数功能:
该函数的功能是根据哈夫曼树构建与之对应的字符-哈夫曼编码对照表。
4)code函数
函数声明:
voidcode(LPCodeNodelist);
参数说明:
list:
字符-哈夫曼编码对照表。
返回值说明:
无返回值。
函数功能:
该函数用于实现编码功能。
5)uncode函数
函数声明:
voiduncode(LPNodetree);
参数声明:
tree:
哈夫曼树。
返回值说明:
无返回值。
函数功能:
该函数用于实现译码功能。
3.算法描述
3.1.哈夫曼树的构建
在本程序中,GetInput函数首先将用户输入的每个字符信息储存到struct_NODE结构中看做是哈夫曼树的叶子结点,并将struct_NODE结构的地址储存到struct_CONTAINER结构中,按字符出现频率升序插入到双向链表中,然后调用createHuffmanTree函数构造哈夫曼树。
在构造哈夫曼树的过程中,首先从双向链表中选取字符出现频率最小和第二小的结点,从中提取哈夫曼树的子树,将两个子树合并成一个子树,再将父节点的地址存入struct_CONTAINER结构中,并插入到双向链表中。
重复此步骤直到链表中只剩下一个结点。
这样该struct_CONTAINER结构中存储的struct_NODE类型的指针就指向要得到哈夫曼树的根节点了。
3.2.字符-哈夫曼编码对照表
用深度优先搜索的方法递归的遍历哈夫曼树,展开的过程中向调用的递归函数传递要访问的结点的哈夫曼编码。
当访问叶子结点时,从结点中提取字符信息,并和其哈夫曼编码一同储存到struct_CODENODE结构中,然后将struct_CODENODE结构插入到单链表中。
如此,当遍历完成时,字符-哈夫曼编码对照表便构造完成了。
3.3.编码
从源文件中读取一个字符,在字符-哈夫曼编码对照表中查找该字符,将查找到的结点中储存的哈夫曼编码写入到目标文件中。
图31构建哈夫曼树流程图图32编码流程图
3.4.译码
从源文件中读取字符,按照字符的指示访问哈夫曼树的子树,即从根节点出发,若读取到‘0’则访问左子树,若读取到‘1’则访问右子树,知道子树为哈夫曼树的叶子结点为止,此时向目标文件中输出结点中的字符。
图33译码流程图
4.程序测试
4.1.字符集输入
输入的字符集及字符出现频率如表4-1所示:
表41字符集输入用例
字符
a
b
c
出现频率
1
2
3
程序运行效果如图4-1所示:
图41程序运行效果图
4.2.编码测试
运行程序编码模块,如图4-2所示:
图42程序运行编码模块效果图
程序编码输入文件如图4-3所示:
图43程序编码输入文件截图
输出分析如表4-3所示:
表43编码输出分析
a
b
c
b
c
a
a
c
b
1
00
01
00
01
1
1
01
00
程序编码输出文件如图4-4所示:
图44程序编码输出文件截图
4.3.译码测试
将编码后的文件逆向输入进行译码。
运行程序译码模块,如图4-5所示:
图45程序运行译码模块效果图
程序译码输入文件如图4-6所示:
图46程序译码输入文件截图
程序译码输出文件如图4-7所示:
图47程序译码输出文件截图
4.4.图形演示界面
图形演示界面如图4-8所示:
图4-8图形演示界面截图
参考文献
[1]严蔚敏,吴伟民.数据结构(C语言版)[M].北京:
清华大学出版社,2006
[2]吕国英.算法设计与分析[M].北京:
清华大学出版社,2006
[3]徐宝文,李志.C程序设计语言[M].北京:
机械工业出版社,2004
[4]ErichGamma,RichaedHelm.设计模式(英文版)[M].北京:
机械工业出版社,2004
附录(程序清单)
#include
#include
#include
#include//图形文件
#defineR15
typedefstruct_NODE{
charword;
intvalue;
_NODE*left,*right;
}Node,*LPNode;
typedefstructpoint
{
intx,y;//树节点坐标
}point;
typedefstruct_CONTAINER{
LPNodev;
struct_CONTAINER*last,*next;
}Container,*LPContainer;
typedefstruct_CODENODE{
charword;
charcode[100];
struct_CODENODE*next;
}CodeNode,*LPCodeNode;
voidinsert(LPContainerlist,LPContainernode)
{
LPContainerp;
p=list->last;
while(node->v->valuev->value)
{
p=p->last;
}
node->last=p;
node->next=p->next;
p->next->last=node;
p->next=node;
}
LPNodecreateHuffmanTree(LPContainerlist)
{
LPContainerp;
LPNodeleft,right,t;
while(list->next!
=list->last)
{
p=list->next;
list->next=p->next;
left=p->v;
free(p);
p=list->next;
list->next=p->next;
list->next->last=list;
right=p->v;
t=(LPNode)malloc(sizeof(Node));
t->word=-1;
t->value=left->value+right->value;
t->left=left;
t->right=right;
p->v=t;
insert(list,p);
}
p=list->next;
list->next=p->next;
list->next->last=list;
left=p->v;
free(p);
returnleft;
}
LPNodeGetInput()
{
Containerlist;
LPContainerp;
LPNodehead;
inti,num;
printf("输入字符集规模:
");
scanf("%d",&num);
list.v=(LPNode)malloc(sizeof(Node));
list.v->word=-1;
list.v->value=0;
list.v->left=list.v->right=NULL;
list.next=&list;
list.last=&list;
for(i=0;i{
p=(LPContainer)malloc(sizeof(Container));
p->v=(LPNode)malloc(sizeof(Node));
p->v->left=p->v->right=NULL;
getchar();
printf("输入字符:
");
scanf("%c",&p->v->word);
printf("输入该字符的权值:
");
scanf("%d",&p->v->value);
insert(&list,p);
}
printf("正在构造哈夫曼树……\n");
head=createHuffmanTree(&list);
printf("哈夫曼树创建成功!
\n");
free(list.v);
returnhead;
}
voiddfs(LPNodet,char*code,LPCodeNodelist)
{
LPCodeNodep;
charl[100],r[100];
if(t->word!
=-1)
{
p=(LPCodeNode)malloc(sizeof(CodeNode));
p->word=t->word;
strcpy(p->code,code);
p->next=list->next;
list->next=p;
return;
}
strcpy(l,code);
strcat(l,"0");
dfs(t->left,l,list);
strcpy(r,code);
strcat(r,"1");
dfs(t->right,r,list);
}
LPCodeNodecreateCodeList(LPNodetree)
{
CodeNodehead;
head.next=NULL;
dfs(tree,"",&head);
returnhead.next;
}
voidcode(LPCodeNodelist)
{
FILE*sfp,*dfp;
charpath[256],c;
LPCodeNodep;
printf("请输入源文件路径:
");
scanf("%s",path);
sfp=fopen(path,"rt");
printf("请输入目标文件路径:
");
scanf("%s",path);
dfp=fopen(path,"wt");
while((c=fgetc(sfp))!
=EOF)
{
p=list;
while(p->word!
=c)
p=p->next;
fputs(p->code,dfp);
}
fclose(sfp);
fclose(dfp);
}
voiduncode(LPNodetree)
{
FILE*sfp,*dfp;
charpath[256],c;
LPNodep;
printf("请输入源文件路径:
");
scanf("%s",path);
sfp=fopen(path,"rt");
printf("请输入目标文件路径:
");
scanf("%s",path);
dfp=fopen(path,"wt");
p=tree;
while((c=fgetc(sfp))!
=EOF)
{
if(c=='0')
p=p->left;
else
p=p->right;
if(p->word!
=-1)
{
fputc(p->word,dfp);
p=tree;
}
}
fclose(sfp);
fclose(dfp);
}
voidprint(LPCodeNodelist)
{
printf("\n字符-哈夫曼编码对照表\n");
while(list!
=NULL)
{
printf("%c--%s\n",list->word,list->code);
list=list->next;
}
printf("\n");
}
voidwindow()//初始化图形眶
{
initgraph(480,320,SHOWCONSOLE);
setcolor(YELLOW);
pointdian[6];
dian[0].x=192;
dian[0].y=96;
dian[1].x=128;
dian[1].y=144;
dian[2].x=256;
dian[2].y=144;
dian[3].x=220;
dian[3].y=192;
dian[4].x=320;
dian[4].y=192;
for(inti=0;i<5;i++)
{
circle(dian[i].x,dian[i].y,R);
floodfill(dian[i].x,dian[i].y,YELLOW);//填充颜色
Sleep(1000);
}
line(dian[2].x,dian[2].y,dian[3].x,dian[3].y);
line(dian[2].x,dian[2].y,dian[4].x,dian[4].y);
Sleep(1000);
line(dian[0].x,dian[0].y,dian[1].x,dian[1].y);
line(dian[0].x,dian[0].y,dian[2].x,dian[2].y);
}
voidmain()
{
LPNodetree=NULL;
LPCodeNodelist=NULL;
intt;
while
(1)
{
printf("*******************************************");
printf("\n*哈夫曼编/译器*\n");
printf("\n*1.输入字符集*\n*2.编码*\n*3.解码*\n*4.演示*\n*5.退出*\n");
printf("**");
printf("\n*******************************************\n");
printf("\n请输入操作码:
");
scanf("%d",&t);
switch(t)
{
case1:
tree=GetInput();
list=createCodeList(tree);
print(list);
break;
case2:
code(list);
break;
case3:
uncode(tree);
break;
case4:
window();
break;
case5:
exit(0);
}
}
}
课程设计总结:
通过此次课程设计,是我在数据结构的设计和算法设计等方面有了进一步的提高。
关于数据结构方面,我对于链表和树结构的理解更加深刻,链表的建立、遍历、插入等操作;树的建立、遍历等操作更加熟练,已可以运用自如。
关于算法方面,我对于插入排序,深度优先搜索等算法,已理解其原理,并能熟练编写。
同时,我还深刻认识到,就我们计算机专业的学生来说,只是掌握经典的算法和数据结构是远远不够的。
我们要深刻发掘创新能力,培养创新思维,只有这样我们才能在编程解决实际问题的道路上走的更远。
指导教师评语:
指导教师(签字):
年月日
课程设计成绩