说明书2.docx
《说明书2.docx》由会员分享,可在线阅读,更多相关《说明书2.docx(32页珍藏版)》请在冰豆网上搜索。
![说明书2.docx](https://file1.bdocx.com/fileroot1/2023-6/28/068c68e3-5599-44e3-8c2d-17ea17a51c73/068c68e3-5599-44e3-8c2d-17ea17a51c731.gif)
说明书2
摘要
随着计算机的普遍应用与日益发展,其应用早已不局限于简单的数值运算,而涉及到问题的分析、数据结构框架的设计以及设计最短路线等复杂的非数值处理和操作。
算法与数据结构的学习就是为以后利用计算机资源高效地开发非数值处理的计算机程序打下坚实的理论、方法和技术基础。
算法与数据结构旨在分析研究计算机加工的数据对象的特性,以便选择适当的数据结构和存储结构,从而使建立在其上的解决问题的算法达到最优。
数据结构是在整个计算机科学与技术领域上广泛被使用的术语。
它用来反映一个数据的内部构成,即一个数据由那些成分数据构成,以什么方式构成,呈什么结构。
数据结构有逻辑上的数据结构和物理上的数据结构之分。
逻辑上的数据结构反映成分数据之间的逻辑关系,而物理上的数据结构反映成分数据在计算机内部的存储安排。
数据结构是数据存在的形式。
《数据结构》主要介绍一些最常用的数据结构,阐明各种数据结构内在的逻辑关系,讨论其在计算机中的存储表示,以及在其上进行各种运算时的实现算法,并对算法的效率进行简单的分析和讨论。
数据结构是介于数学、计算机软件和计算机硬件之间的一门计算机专业的核心课程,它是计算机程序设计、数据库、操作系统、编译原理及人工智能等的重要基础,广泛的应用于信息学、系统工程等各种领域。
学习数据结构是为了将实际问题中所涉及的对象在计算机中表示出来并对它们进行处理。
通过课程设计可以提高学生的思维能力,促进学生的综合应用能力和专业素质的提高。
关键词:
哈夫曼;二叉树;字符;二进制编码
目录
1绪论………………………………………………………………………………1
2采用类C语言定义相关的数据类型……………………………………………2
3各模块的伪码算法………………………………………………………………3
3.1哈夫曼树的存储结构……………………………………………………3
3.2哈弗曼树的算法……………………………………………………………3
3.3哈弗曼编码………………………………………………………………4
3.4哈弗曼译码………………………………………………………………6
3.5主函数……………………………………………………………………7
3.6显示部分源程序…………………………………………………………7
4函数的调用关系图………………………………………………………………9
5调试分析…………………………………………………………………………10
5.1调试中遇到的问题及对问题的结决办法………………………………10
5.2算法的时间复杂度和空间复杂度………………………………………11
6测试结果…………………………………………………………………………12
6.1显示菜单…………………………………………………………………12
6.2显示编码…………………………………………………………………13
6.3进行编码…………………………………………………………………14
6.3进行译码…………………………………………………………………15
7设计总结…………………………………………………………………………16
参考文献……………………………………………………………………………17
致谢…………………………………………………………………………………18
附录…………………………………………………………………………………19
1绪论
在科技飞速发展的今天,信息传输显得尤为重要,这主要体现在计算机之间的数据传输,主要包括文字,声音,图像,视频等文件的传输,而计算机不能直接处理这些信息,计算机只能识别二进制数据。
例如文字,在数据通讯中,传送方要将传送的文字转换成由二进制字符0、1组成的二进制串,而接受方要将二进制字符0、1组成的二进制串还原成对应的文字形式。
于是便完成了文字数据的传输过程。
时下,数据结构是一门随着计算机科学的发展而逐渐形成的新兴学科。
随着计算机技术的发展,计算机的应用领域从最初的科学计算发展到人类社会的各个领域,计算机处理的对象也由纯粹的数值发展到字符、表格、图像和声音等各种具有一定结构的数据,这就给程序设计带来一些新的问题。
与飞速发展的计算机硬件相比,计算机软件的发展相对缓慢。
研究数据结构可以解决计算机之间的数据传输效率低下的问题。
在数据结构中,研究树形结构对数据编码有很大作用,对数据构造二叉树编码应用广泛。
例如对字符进行传输时,可以设计编码二叉树对字符进行二进制编码转换成二进制数据后再进行传输。
哈夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。
哈夫曼压缩属于可变代码长度算法一族。
意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。
因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。
2采用类C语言定义相关的数据类型
2.1内部数据类型
chardata;//结点值,用字符类型表示
intweight;//权值
intparent;//双亲结点
intlchild;//左孩子结点
intrchild;
2.2自定义的数据类型
typedefstruct
3各模块的伪码算法
3.1哈夫曼树的存储结构
#defineN50//叶子结点数
#defineM2*N-1//哈夫曼树中结点总数
typedefstruct{
intweight;//叶子结点的权值
intlchild,rchild,parent;//左右孩子及双亲指针
}HTNode;//树中结点类型
typedefHTNodeHuffmanTree[M+1];
3.2哈弗曼树的算法
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;//父节点的左节点和右节点
}
}
3.3哈弗曼编码
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.4哈弗曼译码
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];
}
}
}
}
3.5主函数
voidmain()
{
intn=26,i;
charorz,back,flag=1;
charstr[]={'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};//初始化
intfnum[]={186,64,13,22,32,103,21,15,47,57,1,2,32,20,57,63,15,1,48,51,80,23,8,18,1,16};//初始化
HTNodeht[M];//建立结构体
HCodehcd[N];//建立结构体
for(i=0;i{
ht[i].data=str[i];
ht[i].weight=fnum[i];
}
while(flag)//菜单函数,当flag为0时跳出循环
3.6显示部分源程序:
{
printf("\n");
printf("********************************");
printf("\n**1---------------显示编码**");
printf("\n**2---------------进行编码**");
printf("\n**3---------------进行译码**");
printf("\n**4---------------退出**\n");
printf("***********************************");
printf("\n");
printf("请输入选择的编号:
");
scanf("%c",&orz);
switch(orz)
{
case'a':
case'A':
system("cls");//清屏函数
CreateHT(ht,n);
CreateHCode(ht,hcd,n);
DispHCode(ht,hcd,n);
printf("\n按任意键返回...");
getch();
system("cls");
break;
case'b':
case'B':
system("cls");
printf("请输入要进行编码的字符串(以#结束):
\n");
editHCode(ht,hcd,n);
printf("\n按任意键返回...");
getch();
system("cls");
break;
case'c':
case'C':
system("cls");
DispHCode(ht,hcd,n);
printf("请输入编码(以#结束):
\n");
deHCode(ht,hcd,n);
printf("\n按任意键返回...");
getch();
system("cls");
break;
case'd':
case'D':
flag=0;
break;
default:
system("cls");
}}}
4函数的调用关系图
图4.1函数的调用关系图
图4.2建立哈夫曼树的算法流程图
图4.3构建哈夫曼编码表的算法流程图
图4.4编码算法流程图
图4.5解码算法流程图
5调试分析
5.1调试中遇到的问题及对问题的结决办法
这部分我主要遇到了如下两个问题,其内容与解决方法如下所列:
第一个问题是权重的筛选部分出现了错误
解决办法:
一开始对于筛选最小权重的代码编写如下:
voidSelectMin(HFMTT,inti,int*p1,int*p2)
{
intj,min=999;
for(j=0;j
{
if(T[j].parent==-1&&min>T[j].weight)
{
min=T[j].weight;
*p1=j;
}
}
min=999;
for(j=0;j
{
if(T[j].parent==-1&&min>T[j].weight&&j!
=(*p1))
{
min=T[j].weight;
*p2=j;
}
}
}
因为权重中最大的就是字符e的权重103,所以为初始值min赋值时觉得999就已经是无限大了。
但是后来发现编码不知确,就开始思考是什么问题。
发现每次筛选都将会把最小的两个权重进行相加,所以很快就会超过999,编码自然就出现了问题。
所以后来将min定义成了long型,并赋值999999,问题就解决了。
第二个问题是生成编码表的时候如何将逆向编码正向存储
解决办法:
对于求编码的时候,由于是从叶子节点向根顺次而求,所以编码结果将是逆向的。
一开始想到的办法是利用栈的结构,将编码依次存入栈中,再在一个字符编码结束时将栈倒空,这样就可以将编码正向存储了。
但是又在考虑如果不用栈时候也可以做到。
后来想到了strcpy函数对字符数组进行链接。
所以就可以定义一个数组,从后向前存储编码,再在一个字符编码结束时将这个数组有值的重新存入新数组中,即可以成为正向编码了。
最终实现编码如下:
HFCodehfEn(HFMTT)
{
inti,f,c,start;
HFCodehc;
char*cd;
hc=(char*)malloc((N+1)*sizeof(char*));
cd=(char)malloc(N*sizeof(char));
cd[N-1]='\0';
for(i=0;i{
start=N-1;
for(c=i,f=T[i].parent;f!
=-1;c=f,f=T[f].parent)
{
if(T[f].left==c)
{
cd[--start]='0';
}
else
{
cd[--start]='1';
}
}
hc[i]=(char*)malloc((N-start)*sizeof(char));
strcpy(hc[i],&cd[start]);
}
returnhc;
}
5.2算法的时间复杂度和空间复杂度
在统计字符出现频率的算法中,是一个二重循环,时间复杂度为O(len*len),len是字符串的长度。
在构造哈夫曼树的算法中,需要找出没有扩展过且当前最小的两个节点组成新节点,这里需要遍历的时间复杂度为O(n*n),n为叶子节点的个数。
最后对输入字符串编码的时间复杂度为O(len*n),因为对每一个字符都需要查找一次哈夫曼编码表。
数据结构采用了一维数组hnode[2n-1]来存储n个叶子节点形成的哈夫曼树,空间复杂度为O(n);同时利用了一个hc[n*n]的二维数组来存储每个叶子节点对应的哈夫曼编码表,空间复杂度为O(n*n)。
6测试结果
6.1显示菜单
6.2.显示编码
6.3.进行编码
6.4.进行译码
7设计总结
本次课程设计涵盖了对字符及其使用频度构造哈夫曼树和哈夫曼编码表,再对输入字符进行哈夫曼编码,将编码写入文件进而读取文件并译码等模块功能,整个过程将结构体、指针、数组、语句循环选择结构,链表,文件读写等知识联系在一起,考察了我们运用C++语言的能力以及对数据结构的理解,通过几天的编写和调试,基本上实现了数据传输的过程。
而在这个过程中,我开始进展十分缓慢,主要是因为首次接触有关程序实现编码的问题,对树形结构也不怎么了解,于是在第一步构造哈夫曼树的时候就花了很长时间来理解哈夫曼算法,哈夫曼树构造函数里面设置了很多变量,那个核心部分怎么也看不懂,我带着疑惑地将代码全都打出来,运行成功后我对着结果一步一步列出其中的过程,在循环中确定每一次各变量的值,对照着事先画好的哈夫曼树仔细看了看,终于了解了各变量的作用,哈夫曼树构造的原理大致也就清楚了,于是后面的哈夫曼编码表结构,哈夫曼译码过程也迎刃而解,整个过程的原理就把握住了。
在上机调试的时候,也屡次出行过错误,例如对字符进行哈夫曼编码的时候,是从哈夫曼树的根结点开始沿父亲链往上回溯的,于是这样得到的编码实际上是反过来的,但用来存储它们的位串数组也不一般,在编码表结构里还定义了一个位置变量start,用以指示哈夫曼编码在数组中的起始位置,start是从最后一个开始指向的,即从后面开始存储二进制编码,于是从前面开始读取就能获得字符的哈夫曼编码。
通过这样不断的调试,我对整个结构的理解就越来越清楚,经过几天的努力,一个小型的哈夫曼编译码系统就完成了。
整个系统能实现对任意输入的空格或26个大写英文字符进行哈弗曼编码,再写入文件,最后读取文件并译码的功能。
但仍有很多方面做的还存在缺陷,例如系统能翻译出的二进制位数不能大于1000,不能输入过多的字符,还有在程序界面也没怎么规划,今后还将努力改善。
参考文献
[1]张俊、张彦铎.C++面向对象程序设计.北京:
中国铁道出版社,2008.
[2]田鲁怀.数据结构.北京:
电子工业出版社,2006.
[3]唐策善,黄秋生.数据结构.北京:
中国科技大学出版社,1992.
[4]黄杨铭.数据结构.北京:
科学出版社,2001.
[5]徐孝凯.数据结构实用教程(C/C++描述).北京:
清华大学出版社,1999.
[6]黄秋生.数据结构.北京:
经济科学出版社,1999.
[7]徐士良.使用数据结构.北京:
清华大学出版社,2000.
[8]李春葆.数据结构考研辅导书.北京:
清华大学出版社,2003.
[9]李春葆.数据结构习题解析(C语言版).北京:
清华大学出版社,2000
致谢
在此次课程设计中,我们衷心感谢贾老师对我们的细心指导。
贾老师指引我们的课程设计的写作的方向和架构,并指正出其中误谬之处,使我们一起努力完成,贾老师要指导很多同学的论文,加上本来就有的教学任务,工作量之大可想而知,老师的用心良苦。
在此,谨向贾老师衷心的感谢!
谢谢贾老师在我们的课程设计过程中给与我们的极大地帮助。
同时,课程设计的顺利完成,离不开小组内部成员的互相努力,团结协作。
在整个的课程设计写作中,大家互相支持,努力需找问题答案所在。
最终顺利完成了这个课程设计。
在文档的写作过程中也学到了做任何事情所要有的态度和心态,首先做学问要一丝不苟,对于发展过程中出现的任何问题和偏差都不要轻视,要通过正确的途径去解决,在做事情的过程中要有耐心和毅力,不要一遇到困难就达退堂鼓,只要坚持下去就可以找到思路去解决问题的。
而且要学会与人合作,这样做起事情来就可以事半功倍。
附录
#include
#include//要用system函数要调用的头文件
#include//用getch()要调用的头文件
#include
#defineN50//义用N表示50叶节点数
#defineM2*N-1//用M表示节点总数当叶节点数位n时总节点数为2n-1
#defineMAXSIZE100
typedefstruct
{
chardata;//结点值
intweight;//权值
intparent;//双亲结点
intlchild;//左孩子结点
intrchild;//右孩子结点
}HTNode;
typedefstruct
{
charcd[N];//存放哈夫曼码
intstart;//