数据结构实践环节实验报告课程设计.docx
- 文档编号:6598737
- 上传时间:2023-01-08
- 格式:DOCX
- 页数:26
- 大小:124.63KB
数据结构实践环节实验报告课程设计.docx
《数据结构实践环节实验报告课程设计.docx》由会员分享,可在线阅读,更多相关《数据结构实践环节实验报告课程设计.docx(26页珍藏版)》请在冰豆网上搜索。
数据结构实践环节实验报告课程设计
洛阳理工学院
课程设计说明书
课程名称_________数据结构_______________
设计课题________哈夫曼编/译码器_________
专业______计算机科学与技术_________
班级________B12xxxx_____________
学号_______B12xxxxxxx________________
姓名____xxx______________
完成日期2014年6月14日
课程设计任务书
设计题目:
_____________哈夫曼编/译码器___________________
_________________________________________________________
设计内容与要求:
设计内容:
打开一篇英文文章,统计该文章中每个字符出现的次数,然后以它们作为权值,设计一个哈夫曼编/译码系统。
要求:
以每个字符出现的次数为权值,建立哈夫曼树,求出哈夫曼编码,对文件yuanwen中的正文进行编码,将结果存到文件yiwen中,再对文件yiwen中的代码进行译码,结果存到textfile中。
指导教师:
xxxx
2014年6月5日
课程设计评语
成绩:
指导教师:
年月日
【问题描述】
打开一篇英文文章,统计该文章中每个字符出现的次数,然后以它们作为权值,设计一个哈夫曼编/译码系统。
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
这要求在发送端通过一个编码系统对待传输数据预先编码,在接收端将传来的数据进行译码(复原)。
对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。
试为这样的信息收发站编写一个哈夫曼码的编/译码系统。
【基本要求】
以每个字符出现的次数为权值,建立哈夫曼树,求出哈夫曼编码,对文件yuanwen中的正文进行编码,将结果存到文件yiwen中,再对文件yiwen中的代码进行译码,结果存到textfile中。
一个完整的系统应具有以下功能:
(1)I:
初始化(Initialization)。
从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
(2)E:
编码(Encoding)。
利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
(3)D:
译码(Decoding)。
利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件Textfile中。
【测试数据】
用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下英文的编码和译码:
“Ilikeplayingfootball”。
字符
A
B
C
D
E
F
G
H
I
J
K
L
M
频度
186
64
13
22
32
103
21
15
47
57
1
5
32
20
字符
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
频度
57
63
15
1
48
51
80
23
8
18
1
16
1
【算法思想】
哈夫曼编\译码器的主要功能是先建立哈夫曼树,然后利用建好的哈夫曼树生成哈夫曼编码后进行译码。
在数据通信中,经常需要将传送的文字转换成由二进制字符0、1组成的二进制串,称之为编码。
构造一棵哈夫曼树,规定哈夫曼树中的左分之代表0,右分支代表1,则从根节点到每个叶子节点所经过的路径分支组成的0和1的序列便为该节点对应字符的编码,称之为哈夫曼编码。
最简单的二进制编码方式是等长编码。
若采用不等长编码,让出现频率高的字符具有较短的编码,让出现频率低的字符具有较长的编码,这样可能缩短传送电文的总长度。
哈夫曼树课用于构造使电文的编码总长最短的编码方案。
(1)其主要流程图如图1-1所示。
(2)设计包含的几个方面:
①赫夫曼树的建立
哈夫曼树的建立由赫夫曼算法的定义可知,初始森林中共有n棵只含有根结点的二叉树。
算法的第二步是:
将当前森林中的两棵根结点权值最小的二叉树,合并成一棵新的二叉树;每合并一次,森林中就减少一棵树,产生一个新结点。
显然要进行n-1次合并,所以共产生n-1个新结点,它们都是具有两个孩子的分支结点。
由此可知,最终求得的哈夫曼树中一共有2n-1个结点,其中n个结点是初始森林的n个孤立结点。
并且赫夫曼树中没有度数为1的分支结点。
我们可以利用一个大小为2n--1的一维数组来存储赫夫曼树中的结点。
②哈夫曼编码
要求电文的哈夫曼编码,必须先定义哈夫曼编码类型,根据设计要求和实际需要定义的类型如下:
typedetstruct{
charch;//存放编码的字符
charbits[N+1];//存放编码位串
intlen;//编码的长度
}CodeNode;//编码结构体类型
③代码文件的译码
译码的基本思想是:
读文件中编码,并与原先生成的哈夫曼编码表比较,遇到相等时,即取出其对应的字符存入一个新串中。
【模块划分】
1)问题分析哈夫曼树的定义
1.哈夫曼树节点的数据类型定义为:
typedefstruct{//哈夫曼树的结构体
charch;
intweight;//权值
intparent,lchild,rchild;
}htnode,*hfmtree;
2)所实现的功能函数如下
1、voidhfmcoding(hfmtree&HT,hfmcode&HC,intn)初始化哈夫曼树,处理InputHuffman(HuffmanHfm)函数得到的数据,按照哈夫曼规则建立2叉树。
此函数块调用了Select()函数。
2、voidSelect(hfmtree&HT,inta,int*p1,int*p2)//Select函数,选出HT树到a为止,权值最小且parent为0的2个节点
3、Encoding
编码功能:
对输入字符进行编码
4、Decoding
译码功能:
利用已建好的哈夫曼树将文件codefile.txt中的代码进行译码,结果存入文件textfile.dat中。
5.主函数的简要说明,主函数主要设计的是一个分支语句,让用户挑选所实现的功能。
使用链树存储,然后分别调用统计频数函数,排序函数,建立哈夫曼函数,编码函数,译码函数来实现功能。
3)系统功能模块图:
【数据结构】
(1)①哈夫曼树的存储结构描述为:
#defineN50//叶子结点数
#defineM2*N-1//哈夫曼树中结点总数
typedefstruct{
intweight;//叶子结点的权值
intlchild,rchild,parent;//左右孩子及双亲指针
}HTNode;//树中结点类型
typedefHTNodeHuffmanTree[M+1];
②哈夫曼树的算法
voidCreateHT(HTNodeht[],intn)//调用输入的数组ht[],和节点数n
{
inti,k,lnode,rnode;
intmin1,min2;
for(i=0;i<2*n-1;i++)
ht[i].parent=ht[i].lchild=ht[i].rchild=-1;//所有结点的相关域置初值-1
for(i=n;i<2*n-1;i++)//构造哈夫曼树
{
min1=min2=32767;//int的范围是-32768—32767
lnode=rnode=-1;//lnode和rnode记录最小权值的两个结点位置
for(k=0;k<=i-1;k++)
{
if(ht[k].parent==-1)//只在尚未构造二叉树的结点中查找
{
if(ht[k].weight { min2=min1;rnode=lnode; min1=ht[k].weight;lnode=k; } elseif(ht[k].weight { min2=ht[k].weight;rnode=k; } } } ht[lnode].parent=i;ht[rnode].parent=i;//两个最小节点的父节点是i ht[i].weight=ht[lnode].weight+ht[rnode].weight;//两个最小节点的父节点权值为两个最小节点权值之和 ht[i].lchild=lnode;ht[i].rchild=rnode;//父节点的左节点和右节点 } } (2)哈夫曼编码 voidCreateHCode(HTNodeht[],HCodehcd[],intn) { inti,f,c; HCodehc; for(i=0;i { hc.start=n;c=i; f=ht[i].parent; while(f! =-1)//循序直到树根结点结束循环 { if(ht[f].lchild==c)//处理左孩子结点 hc.cd[hc.start--]='0'; else//处理右孩子结点 hc.cd[hc.start--]='1'; c=f;f=ht[f].parent; } hc.start++;//start指向哈夫曼编码hc.cd[]中最开始字符 hcd[i]=hc; } } voidDispHCode(HTNodeht[],HCodehcd[],intn)//输出哈夫曼编码的列表 { inti,k; printf("输出哈夫曼编码: \n"); for(i=0;i { printf("%c: \t",ht[i].data); for(k=hcd[i].start;k<=n;k++)//输出所有data中数据的编码 { printf("%c",hcd[i].cd[k]); } printf("\n"); } } voideditHCode(HTNodeht[],HCodehcd[],intn)//编码函数 { charstring[MAXSIZE]; inti,j,k; scanf("%s",string);//把要进行编码的字符串存入string数组中 printf("\n输出编码结果: \n"); for(i=0;string[i]! ='#';i++)//#为终止标志 { for(j=0;j { if(string[i]==ht[j].data)//循环查找与输入字符相同的编号,相同的就输出这个字符的编码 { for(k=hcd[j].start;k<=n;k++) { printf("%c",hcd[j].cd[k]); } break;//输出完成后跳出当前for循环 } } } } (3)哈夫曼译码 voiddeHCode(HTNodeht[],HCodehcd[],intn)//译码函数 { charcode[MAXSIZE]; inti,j,l,k,m,x; scanf("%s",code);//把要进行译码的字符串存入code数组中 while(code[0]! ='#') for(i=0;i { m=0;//m为想同编码个数的计数器 for(k=hcd[i].start,j=0;k<=n;k++,j++)//j为记录所存储这个字符的编码个数 { if(code[j]==hcd[i].cd[k])//当有相同编码时m值加1 m++; } if(m==j)//当输入的字符串与所存储的编码字符串个数相等时则输出这个的data数据 { printf("%c",ht[i].data); for(x=0;code[x-1]! ='#';x++)//把已经使用过的code数组里的字符串删除 { code[x]=code[x+j]; } } } } 【测试情况】 (1)程序运行时,进入的主界面如图: (2)选择1进入,创建名称为yuanwen的文件,如图: (3)输入英语原文为Ilikeplayingfootball,如图所示: (4)程序对原文进行编码运行输出结果,程序如图: 【心得】 在我自己课程设计中,就在编写好源代码后的调试中出现了不少的错误,遇到了很多麻烦及困难,我的调试及其中的错误和我最终找出错误,修改为正确的能够执行的程序中,通过分析,我学到了: 在定义头文件时可多不可少,即我们可多写些头文件,肯定不会出错,但是若没有定义所引用的相关头文件,必定调试不通过; 在执行译码操作时,不知什么原因,总是不能把要编译的二进制数与编译成的字符用连接号连接起来,而是按顺序直接放在一起,视觉效果不是很好。 还有就是,很遗憾的是,我们的哈夫曼编码/译码器没有像老师要求的那样完成编一个文件的功能,这是我们设计的失败之处。 通过本次数据结构的课程设计,我学习了很多在上课没懂的知识,并对求哈夫曼树及哈夫曼编码/译码的算法有了更加深刻的了解,更巩固了课堂中学习有关于哈夫曼编码的知识 【源程序】 #include #include #include #defineN5000 #definem128//叶子结点个数,即字符总类数 #defineM2*m-1//哈夫曼树的节点数 charCH[N];//记录原文字符数组 charYW[N];//记录译文字符数组 typedefchar*Hcode[m+1];//存放哈夫曼字符编码串的头指针的数组 typedefstruct { chara; intnum; }dangenode;//记录单个字符的类别和出现的次数 typedefstruct { dangenodeb[129]; inttag; }jilunode;//统计原文出现的字符种类数 typedefstructnode { intweight;//结点权值 intparent;//双亲下标 intLchild;//左孩子结点的下标 intRchild;//右孩子的下标 }htnode,hn[M];//静态三叉的哈夫曼树的定义 voidjianliwenjian() { FILE*fp; printf("现在建立文件,以存储原文(文件名称默认为yuanwen)\n"); printf("文件建立中......\n"); if((fp=fopen("yuanwen","wb"))==NULL)//建立文件 { printf("Cannotopenfile\n"); exit(0); } printf("文件已建立,名称为: yuanwen"); fclose(fp);//关闭文件 } voidluruyuanwen()//原文输入完后,将其保存到文件yuanwen中 { charch; FILE*fp; if((fp=fopen("yuanwen","wb"))==NULL)//打开文件 { printf("Cannotopenfile\n"); exit(0); } printf("请输入原文(结束标志为: ^)\n"); do { ch=getchar(); fputc(ch,fp);//将字符保存到文件中 }while(ch! ='^');//判断结束 getchar();//接收回车命令符 fclose(fp);//关闭文件 } voidmin_2(hnht,intn,int*tag1,int*tag2)//在建哈夫曼树的过程中,选择权值小的两 {//个结点 inti,min,s; min=N; for(i=1;i<=n;i++) if(ht[i].weight { min=ht[i].weight; *tag1=i;//记录权值最小的结点下标 } min=N; for(i=1;i<=n;i++) if(ht[i].weight =*tag1) { min=ht[i].weight; *tag2=i; } if(ht[*tag1].weight==ht[*tag2].weight&&ht[*tag2].Lchild! =0) { s=(*tag1);//如果结点权值相同,先出现的放在哈夫曼树的左边 (*tag1)=(*tag2); (*tag2)=s; } } voidchuangjian(jilunode*jilu,hnht)//建立哈夫曼树、以及对原 { FILE*fp;//文字符的类别和数量统计 inti=-1,j,s=0,tag1,tag2; //s=0; //i=-1; //charch; if((fp=fopen("yuanwen","rb"))==NULL)//以只读的方式打开文件 { printf("Cannotopenfile\n"); exit(0); } while(! feof(fp))//判断文件指示标志是否移动到了文件尾处 { charch; ch=fgetc(fp); if(ch! ='^')//判断字符是否是结束标志 { ++i; CH[i]=ch; for(j=1;j<=jilu->tag;j++) { if(CH[i]==jilu->b[j].a) { jilu->b[j].num++; break; } } if(j-1==jilu->tag&&CH[i]! =jilu->b[j-1].a) { jilu->tag++; jilu->b[jilu->tag].a=CH[i]; jilu->b[jilu->tag].num=1; } } } jilu->tag--; fclose(fp);//关闭文件 printf("原文中的各字符统计状况如下: \n"); printf("*^*^*^*^*^*^*^*^**^*^*^**^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*\n"); for(i=1;i<=jilu->tag;i++) { s++; printf("'%c'的个数为: %d",jilu->b[i].a,jilu->b[i].num); if(s%4==0)//每行放四个数据 printf("\n"); } printf("\n"); for(i=1;i<=2*(jilu->tag)-1;i++) { if(i<=jilu->tag) { ht[i].weight=jilu->b[i].num;//初始化叶子结点权值 ht[i].Lchild=0;//初始化叶子结点左孩子 ht[i].parent=0;//初始化叶子结点父母 ht[i].Rchild=0;//初始化叶子结点右孩子 } else { ht[i].Lchild=0;//初始化非叶子结点左孩子 ht[i].parent=0;//初始化非叶子结点父母 ht[i].Rchild=0;//初始化非叶子结点右孩子 ht[i].weight=0;//初始化非叶子结点权值 } } for(i=jilu->tag+1;i<=2*(jilu->tag)-1;i++) { min_2(ht,i-1,&tag1,&tag2);//需找权值小的两个父母为0的结点 ht[tag1].parent=i; ht[tag2].parent=i; ht[i].Lchild=tag1; ht[i].Rchild=tag2; ht[i].weight=ht[tag1].weight+ht[tag2].weight; } } voidbianma(jilunode*jilu,hnht,Hcodehc,intn)//哈夫曼树建完后,对叶 {//子结点逐个编码 char*cd; intstart,i,p,c; cd=(char*)malloc((n+1)*sizeof(char));//申请存储字符的临时空间 cd[n-1]='\0';//加结束标志 for(i=1;i<=n;i++) { start=n-1; c=i; p=ht[i].parent; while(p! =0) { --start; if(ht[p].Lchild==c) cd[start]='1';//结点在左边置1 if(ht[p].Rchild==c) cd[start]='0';//结点在右边置0 c=p; p=ht[p].parent; } printf("%c的编码为: %s\n",jilu->b[i].a,&cd[start]); hc[i]=(char*)malloc((n-start)*sizeof(char));//为字符数组分配空间 strcpy(hc[i],&cd[start]);//将临时空间中的编码复制到字符数组中 } free(cd);//释放临时空间 } voidbianmabaocun(Hcodehc,jilunode*jilu)//将原文以编码的形式保存到 {//文件yiwen中 inti,j; FILE*fp; if((fp=fopen("yiwen","wb"))==NULL)//以写的方式打开文件 { printf("Cannotopenfile\n"); exit(0);//文件打开失败退出 } for(i=0;i<=N&&CH
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 实践 环节 实验 报告 课程设计