数据结构实践环节实验报告课程设计.docx

上传人:b****5 文档编号:6598737 上传时间:2023-01-08 格式:DOCX 页数:26 大小:124.63KB
下载 相关 举报
数据结构实践环节实验报告课程设计.docx_第1页
第1页 / 共26页
数据结构实践环节实验报告课程设计.docx_第2页
第2页 / 共26页
数据结构实践环节实验报告课程设计.docx_第3页
第3页 / 共26页
数据结构实践环节实验报告课程设计.docx_第4页
第4页 / 共26页
数据结构实践环节实验报告课程设计.docx_第5页
第5页 / 共26页
点击查看更多>>
下载资源
资源描述

数据结构实践环节实验报告课程设计.docx

《数据结构实践环节实验报告课程设计.docx》由会员分享,可在线阅读,更多相关《数据结构实践环节实验报告课程设计.docx(26页珍藏版)》请在冰豆网上搜索。

数据结构实践环节实验报告课程设计.docx

数据结构实践环节实验报告课程设计

 

洛阳理工学院

课程设计说明书

课程名称_________数据结构_______________

设计课题________哈夫曼编/译码器_________

专业______计算机科学与技术_________

班级________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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1