华中科技大学数据结构实验报告.docx
《华中科技大学数据结构实验报告.docx》由会员分享,可在线阅读,更多相关《华中科技大学数据结构实验报告.docx(83页珍藏版)》请在冰豆网上搜索。
华中科技大学数据结构实验报告
课程实验报告
课程名称:
数据结构实验
专业班级:
信息安全201502
学号:
姓名:
指导教师:
报告日期:
2016年10月28日
计算机科学与技术学院
1基于顺序存储结构的线性表实现
1.1问题描述
采用顺序表的物理结构,构造一个具有菜单的功能演示系统。
其中,在主程序中完成函数调用所需实参值的准备和函数执行结果的显示。
定义了线性表的初始化表、销毁表、清空表、判定空表、求表长和获得元素等基本运算对应的函数,并给出适当的操作提示显示,可选择以文件的形式进行存储和加载,即将生成的线性表存入到相应的文件中,也可以从文件中获取线性表进行操作。
1.1.1线性表的基本概念
线性表是最常用且最简单的一种数据结构,即n个数据元素的有限序列。
线性表中元素的个数n定义为线性表的长度,n=0时成为空表。
在非空表中的每个数据元素都有一个确定的位置,如a1是第一个数据元素,an是最后一个数据元素,ai是第i个数据元素。
线性表的存储结构分为线性存储和链式存储。
1.1.2逻辑结构与基本运算
线性表的数据逻辑结构定义如下:
ADTList{
数据对象:
D={ai|ai∈ElemSet,i=1,2,……,n,n≥0}
数据关系:
R1={|ai-1,ai∈D,i=2,……,n}
}
依据最小完备性和常用性相结合的原则,以函数形式定义了包括线性表的初
始化表、加载表、保存表、销毁表、清空表、判定空表、求表长、获得元素、查
找元素、获得前驱、获得后继、插入元素、删除元素、遍历表14个基本运算,
要求分别定义函数来实现上述功能,具体功能运算如下:
⑴初始化表:
函数名称是InitaList(L);初始条件是线性表L不存在已存在;操作结果是构造一个空的线性表。
⑵销毁表:
函数名称是DestroyList(L);初始条件是线性表L已存在;操作结果是销毁线性表L。
⑶清空表:
函数名称是ClearList(L);初始条件是线性表L已存在;操作结果是将L重置为空表。
⑷判定空表:
函数名称是ListEmpty(L);初始条件是线性表L已存在;操作结果是若L为空表则返回TRUE,否则返回FALSE。
⑸求表长:
函数名称是ListLength(L);初始条件是线性表已存在;操作结果是返回L中数据元素的个数。
⑹获得元素:
函数名称是GetElem(L,i,e);初始条件是线性表已存在,1≤i≤ListLength(L);操作结果是用e返回L中第i个数据元素的值。
⑺查找元素:
函数名称是LocateElem(L,e,compare());初始条件是线性表已存在;操作结果是返回L中第1个与e满足关系compare()关系的数据元素的位序,若这样的数据元素不存在,则返回值为0。
⑻获得前驱:
函数名称是PriorElem(L,cur_e,pre_e);初始条件是线性表L已存在;操作结果是若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义。
⑼获得后继:
函数名称是NextElem(L,cur_e,next_e);初始条件是线性表L已存在;操作结果是若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,否则操作失败,next_e无定义。
⑽插入元素:
函数名称是ListInsert(L,i,e);初始条件是线性表L已存在且非空,1≤i≤ListLength(L)+1;操作结果是在L的第i个位置之前插入新的数据元素e。
⑾删除元素:
函数名称是ListDelete(L,i,e);初始条件是线性表L已存在且非空,1≤i≤ListLength(L);操作结果:
删除L的第i个数据元素,用e返回其值。
⑿遍历表:
函数名称是ListTraverse(L,visit()),初始条件是线性表L已存在;操作结果是依次对L的每个数据元素调用函数visit()。
1.1.3演示系统与数据文件
要求构造一个具有菜单的功能演示系统。
其中,在主程序中完成函数调用所
需实参值的准备和函数执行结果的显示,并给出适当的操作提示显示。
附录A提
供了简易菜单的框架。
演示系统可选择实现线性表的文件形式保存。
其中,①需要设计文件数据记
录格式,以高效保存线性表数据逻辑结构(D,{R})的完整信息;②需要设计线性
表文件保存和加载操作合理模式。
附录B提供了文件存取的方法。
演示系统可选择实现多个线性表管理。
1.2系统设计
1.2.1数据物理结构
线性表的数据物理结构如下:
typedefstruct{//顺序表(顺序结构)的定义
ElemType*elem;//定义整型指针,为存储空间基址
intlength;//线性表的长度
intlistsize;//当前分配的存储容量(以sizeof(ElemType)为单位)
}SqList;
要实现同时对多个线性表管理,只需要定义一个结构数组即可。
1.2.2演示系统
将菜单演示和用户选择写入到while循环中,用OP获取用户的选择,OP初始化为1,以便第一次能进入循环。
进入循环后系统首先显示功能菜单,然后用户输入选择0-14,其中1-14分别代表线性表的一个基本运算,在主函数中通过SWITCH语句对应到相应的函数功能,执行完该功能后BREAK跳出SWITCH语句,继续执行while循环,直至用户输入0退出当前演示系统。
在第一次进入循环while时首先会询问用户对哪个线性表进行操作,直至退出演示系统之前一直对指定线性表进行操作。
演示系统结构如图1-1.
1.2.3运算算法思想与设计
线性表运算算法思想与设计如下:
1.初始化线性表思想:
将线性表初始化过程写成函数,其中传入函数的参数是主函数中定义的结构型变量L的引用,在函数中,首先使用malloc函数分配LISTSIZE大小的连续内存空间,并将首地址返回赋值给L.elem,由于线性表的长度为0,将L.length初始化为0,即完成了线性表的初始化。
经分析,算法的时间复杂度为O
(1)。
2.销毁线性表思想:
将销毁线性表的过程写成函数,其中传入函数是主函数中定义的结构性变量L。
在函数中,首先使用free函数释放掉以L.elem为首地址的连续内存空间,再将L.length,L.listsize重新赋值为0。
经分析,该算法的时间复杂度为O
(1)。
3.清空线性表的思想:
将清空表的过程写成函数,其中将主函数中定义的结构性变量L的引用作为函数参数,在函数中,由于清空操作并不释放内存空间,故只需将线性表的长度置为0即可。
经分许,该算法的时间复杂度为O
(1)。
4.求线性表表长的思想:
将求表长过程写成函数,其中主函数中定义的结构性变量L的引用作为函数的参数,在函数中,直接返回L.length即为所求线性表的表长。
经分析,该算法的时间复杂度为O
(1)。
5.获得元素的算法思想:
将获得线性表元素写成函数,其中函数的参数是结构型变量L以及数据元素的序号i,由于采取的是线性存储结构,故直接通过访问数组的方式即L.elem[i-1]来获取元素,当然,在这之前需要判断合法性。
经分析,该算法的时间复杂度为O
(1)。
6.查找元素的算法思想:
将查找线性表特定值的数据元素写成函数,其中函数的参数是主函数中定义的结构类型变量L以及查找的数据元素的值,通过循环对线性表中的每一个元素与给定值比较看是否相等,如果相等就返回该元素的次序。
经分析,该算法的时间复杂度为O(n)。
查找算法的流程图如图1-2所示。
图1-2线性表查找算法流程图
7.获得前驱算法思想:
将获得前驱过程写成函数,函数的参数是结构体类型变量以及特定数据元素的值,接受前驱的变量作为另一个参数,首先调用获得元素的函数判断该线性表中特定数据元素的位序,首先判断不为1,否则的话返回FALSE,然后直接返回其前一个元素即L.elem[j-2]。
经分析,该算法的时间复杂度为O(n)。
8.获得后继算法思想:
将获得后继写成函数,函数的参数是结构体类型变量以及特定数据元素的值。
首先判断是否为最后一个元素,如果不是则直接返回其后一个元素,否则的话返回FALSE,同样该算法需要调用获得元素的函数来确定该特定元素在线性表中的次序。
经分析,该算法的时间复杂度为O(n)。
9.插入元素算法思想:
将插入函数写成函数,函数的参数是结构型变量以及插入元素的值大小以及插入位置。
在函数中,首先判断插入位置的合法性,即是否在线性表中合适的位置,其次还要判断当前存储空间是否已满,如果满了的话要malloc函数重新分配空间,插入元素时从该位置起到最后一个元素从后开始以此往后移一个单元。
经分析,该算法的时间复杂度为O(n)。
该算法的程序流程图如图1-3所示。
图1-3线性表中插入元素算法流程图
10.删除元素算法思想:
将删除线性表中元素写成函数,函数的参数是结构类型变量,首先判断位序的合法性,在之后直接将删除元素位置后一个元素直到最后一个元素以此从前往后向前移动一个单元。
经分析,该算法的时间复杂度为O(n)。
11.遍历线性表算法思想:
将遍历线性表写成一个函数,函数的参数是结构类型变量,直接用一个循环来对线性表中的每一个元素进行操作。
经分析,该算法的时间复杂度为O(n)。
1.3系统实现
1.3.1编程环境、运行环境、项目工程描述
本次实验采用MicrosoftVisualStudio2015编程软件编写,并用VS2015进行编译运行,项目名称是lineardatastructer。
1.3.2头文件及预定义常量说明
1.头文件
#include
#include
#include
2.预定义常量
#defineTRUE1
#defineFALSE0
#defineOK1
#defineERROR0
#defineINFEASTABLE-1
#defineOVERFLOW-2
#defineLIST_INIT_SIZE100
#defineLISTINCREMENT10
3.类型表达式
typedefintstatus;
typedefintElemType;
1.3.3系统演示操作
1.系统一开始会显示菜单提示用户输入选择对1-99号线性表哪一个进行操作。
如图1-4所示。
图1-4系统演示1
2.选择对线性表1进行操作,进入菜单演示界面,如图1-5所示。
图1-5系统演示2
3.选择退出0,此时会退出当前演示界面,即退出对当前线性表的操作,并显示要求用户选择对哪一个线性表进行操作。
如图1-6所示。
图1-6演示系统3
4.输入0,退出演示系统,结束操作,如图1-7所示。
图1-7系统演示4
1.3.4测试计划
测试功能及序号
输入要管理的线性表序号
输入函数的参数(具体元素)
预计输出
此时线性表的状态
1.IntiaList
1
无
线性表初始化成功
分配了连续的物理存储空间,表长度为0,表尺寸为空间大小
2.DestroyList
1
无
线性表删除成功
线性表连续物理空间被释放,
3.ClearList
1
无
线性表清空成功
线性表的物理空间保留,但表长置为0.
10.ListInsert(多次调用)
1
输入1:
1,2:
2,3:
3,4:
4,5:
5
线性表创建成功
创建了一个线性表,序列为:
1,2,3,4,5
4.ListEmpty
1
无
输出线性表非空
同上
5.ListLength
1
无
输出线性表的长度为5
同上
6.GetElem
1
输入数据元素的位序为3
输出线性表的第三个元素为3
同上
8.PriorElem
1
输入数据元素的值为4
输出4的前面一个元素是3
同上
9.NextElem
1
输入数据元素的值为2
输出2的后面一个元素是3
同上
11.ListDelete
1
输入数据元素的置为3
输出元素删除成功
重新生成了一新的线性表,序列为1245
7.LocateElem
1
输入数据元素的值为4
输出4是线性表中的第三个数据元素
同上
13.SaveData
1
输入保存到的文件名为LY.txt
输出文件保存成功
同上
14.DataLoading
1
输入要加载的文件名为LY.txt
输出文件加载成功
同上
1.3.5测试
1.输入对线性表1进行操作,进入菜单演示界面,执行功能1,初始化线性表,测试结果如图1-8所示。
图1-8初始化线性表的测试结果
2.执行功能2,销毁线性表,测试结果如图1-9所示。
图1-9删除线性表的操作结果
3.执行功能10,往线性表中添加数据元素1,2,3,4,5,测试结果如图1-10所示。
图1-10插入元素的测试结果
4.执行功能4,判断线性表是否为空,测试结果如图1-11所示。
图1-11判断线性表非空的测试结果
5.执行功能5,求线性表的长度,测试结果如图1-12所示。
图1-12求线性表长度的测试结果
6.执行功能6,查找第三个数据元素的数值,测试结果如图1-13所示。
图1-13查找数据元素的测试结果
7.执行功能8,确定值为4的数据元素前驱数据元素,测试结果如图1-14所示。
图1-14查找元素的前驱数据元素
8.执行功能9,确定值为2的数据元素的后继数据元素,测试结果如图1-15所示。
1-15查找元素的后继数据元素
9.执行功能11,删除第三个数据元素,测试结果如图1-16所示。
图1-16删除线性表中的数据元素测试结果
10.执行功能13,保存线性表中的数据元素值到文件名为LY.txt的文件中,测试结果如图1-17所示。
图1-17保存线性表测试结果
11.清空此时的线性表,在执行功能14,加载线性表,测试结果如图1-18所示。
图1-18线性表加载的测试结果
11.执行功能12,遍历并输出线性表中的元素,应该输出1245,测试结果如图1-19所示。
图1-19遍历输出线性表中元素的测试结果
1.4实验小结
经过本次试验,我充分了解到了线性表的物理结构,并且通过切身的体会熟练掌握了线性表的基本操作,提高了自己写有关线性表的代码的能力,尤其是在写的过程中遇到了许多困难,在多次寻求同学的帮助下终于解决了。
还发现了自己的薄弱之处,就是在文件的处理时存在许多纰漏,导致文件读取不成功。
并且我还加深了对线性表的存在与空的区别。
还有对“&”引用符号的理解,加强了其与“*”的区别,“&e”使得在函数中调用主函数中的值“e”时可以同时更改其值,如在GetElem函数中调用了“e”的值然后在主函数中输出,如果要更改其值的话就必须要用“&”符号。
2基于二叉链表的二叉树实现
2.1问题描述
采用二叉链表作为二叉树的物理结构,实现二叉树的基本运算,数据元素的类型名可自行定义。
要求构造一个具有菜单的功能演示系统,其中,在主程序中完成函数调用所需实参值的准备和函数执行结果的显示,并给出适当的操作提示。
2.1.1二叉树的基本概念
二叉树是一种树型结构,即n个结点的有限集,它的特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。
2.1.2逻辑结构与基本运算
抽象数据类型二叉树的定义如下:
ADTBinaryTree{
数据对象D:
D是具有相同特性的数据元素的集合。
数据关系R:
若D=Φ,则R=Φ,称BinaryTree为空二叉树;
若D≠Φ,则R={H},H是如下二元关系:
(1)在D中存在唯一的成为根的数据元素root,它在关系H中无前驱;
(2)若D-{root}≠Φ,则存在D-{root}={D1,Dr},且D1∩Dr=Φ;
(3)若D1≠Φ,则D1中存在唯一的元素X1,∈H,且存在D1上的关系H1包含于H;若Dr≠Φ,则Dr中存在唯一的元素Xr,∈H,且存在Dr上的关系属于H;
(4)(D,{H1})是一棵符合本定义的二叉树,称为根的左子树,(Dr,{Hr})是一棵符合本定义的二叉树,称为根的右子树。
}
依据最小最小完备性和常用性相结合的原则,以函数形式定义了二叉树的初始化、销毁二叉树、创建二叉树、清空二叉树、判定空二叉树和求二叉树深度等20种基本运算,具体运算功能定义如下:
⑴初始化二叉树:
函数名称是InitBiTree(T);初始条件是二叉树T不存在;操作结果是构造空二叉树T。
⑵销毁二叉:
树函数名称是DestroyBiTree(T);初始条件是二叉树T已存在;操作结果是销毁二叉树T。
⑶创建二叉树:
函数名称是CreateBiTree(T,definition);初始条件是definition给出二叉树T的定义;操作结果是按definition构造二叉树T。
⑷清空二叉树:
函数名称是ClearBiTree(T);初始条件是二叉树T存在;操作结果是将二叉树T清空。
⑸判定空二叉树:
函数名称是BiTreeEmpty(T);初始条件是二叉树T存在;操作结果是若T为空二叉树则返回TRUE,否则返回FALSE。
⑹求二叉树深度:
函数名称是BiTreeDepth(T);初始条件是二叉树T存在;操作结果是返回T的深度。
⑺获得根结点:
函数名称是Root(T);初始条件是二叉树T已存在;操作结果是返回T的根。
⑻获得结点:
函数名称是Value(T,e);初始条件是二叉树T已存在,e是T中的某个结点;操作结果是返回e的值。
⑼结点赋值:
函数名称是Assign(T,&e,value);初始条件是二叉树T已存在,e是T中的某个结点;操作结果是结点e赋值为value。
⑽获得双亲结点:
函数名称是Parent(T,e);初始条件是二叉树T已存在,e是T中的某个结点;操作结果是若e是T的非根结点,则返回它的双亲结点指针,否则返回NULL。
⑾获得左孩子结点:
函数名称是LeftChild(T,e);初始条件是二叉树T存在,e是T中某个节点;操作结果是返回e的左孩子结点指针。
若e无左孩子,则返回NULL。
⑿获得右孩子结点:
函数名称是RightChild(T,e);初始条件是二叉树T已存在,e是T中某个结点;操作结果是返回e的右孩子结点指针。
若e无右孩子,则返回NULL。
⒀获得左兄弟结点:
函数名称是LeftSibling(T,e);初始条件是二叉树T存在,e是T中某个结点;操作结果是返回e的左兄弟结点指针。
若e是T的左孩子或者无左兄弟,则返回NULL。
⒁获得右兄弟结点:
函数名称是RightSibling(T,e);初始条件是二叉树T已存在,e是T中某个结点;操作结果是返回e的右兄弟结点指针。
若e是T的右孩子或者无有兄弟,则返回NULL。
⒂插入子树:
函数名称是InsertChild(T,p,LR,c);初始条件是二叉树T存在,p指向T中的某个结点,LR为0或1,,非空二叉树c与T不相交且右子树为空;操作结果是根据LR为0或者1,插入c为T中p所指结点的左或右子树,p所指结点的原有左子树或右子树则为c的右子树。
⒃删除子树:
函数名称是DeleteChild(T.p.LR);初始条件是二叉树T存在,p指向T中的某个结点,LR为0或1。
操作结果是根据LR为0或者1,删除c为T中p所指结点的左或右子树。
⒄前序遍历:
函数名称是PreOrderTraverse(T,Visit());初始条件是二叉树T存在,Visit是对结点操作的应用函数;操作结果:
先序遍历t,对每个结点调用函数Visit一次且一次,一旦调用失败,则操作失败。
⒅中序遍历:
函数名称是InOrderTraverse(T,Visit));初始条件是二叉树T存在,Visit是对结点操作的应用函数;操作结果是中序遍历t,对每个结点调用函数Visit一次且一次,一旦调用失败,则操作失败。
⒆后序遍历:
函数名称是PostOrderTraverse(T,Visit));初始条件是二叉树T存在,Visit是对结点操作的应用函数;操作结果是后序遍历t,对每个结点调用函数Visit一次且一次,一旦调用失败,则操作失败。
⒇按层遍历:
函数名称是LevelOrderTraverse(T,Visit));初始条件是二叉树T存在,Visit是对结点操作的应用函数;操作结果是层序遍历t,对每个结点调用函数Visit一次且一次,一旦调用失败,则操作失败。
2.1.3演示系统与数据文件
要求构造一个具有菜单的功能演示系统。
其中,在主函数中完成函数调用所需实参值的准备和函数执行结果的显示,并给出适当的操作提示。
附录A中给出了简单菜单的框架。
演示系统可选择实现线性表的文件形式保存,演示系统可选择实现多个线性表的管理。
2.2系统设计
2.2.1数据物理结构
二叉树的数据物理结构如下:
typedefstructBiTNode{
TElemTypedata;
structBiTNode*lchild,*rchild;//左右孩子指针
intindex;
}BiTNode,*BiTree;
//typedef是将结构类型定义structBiTNode取别名为BiTNode
2.2.2演示系统
将菜单演示和用户选择输入写入到while循环中,用op获取用户的选择。
Op初始化为1,以便第一次能进入循环。
进入while循环后,系统首先显示功能菜单,然后提示用户输入选择(0-20),其中1-20对应二叉树的一个基本操作,分别对应一个函数,通过switch语句将用户输入的序号对应到相应的函数功能,执行完该语句后break跳出switch语句,执行while循环,直到用户输入0选择退出,退出系统。
图2-1系统设计结构图
2.2.3运算算法思想与设计
二叉树运算算法思想与设计如下:
1、初始化二叉树算法思想:
将初始化二叉树过程写成函数,函数的参数是主函数中所定义的结构类型指针T(参数传递为引用方式,即取别名),T所指向的是二叉树的根结点,将T赋值为NULL即完成了二叉树的初始化。
经分析,该算法的时间复杂度为O
(1)。
2、销毁二叉树算法思想:
将二叉树的销毁过程写成函数,函数的参数是指向二叉树根结点的结构类型指针T,采取递归的方式先销毁二叉树的左子树,在销毁二叉树的右子树,最后用free函数释放掉根结点对应的内存空间。
经分析,该算法的时间复杂度为O(n)。
3、创建二叉树算法思想:
将二叉树的创建过程写成函数,函数的参数是指向二叉树根结点的结构类型指针T,按照先序次序输入二叉树中结点的值,如果第一个字符为#,则T为空二叉树。
否则,malloc函数分配一个单元的空间作为树的根结点,并为其赋值,采取递归的方式继续创建根结点的左子树和右子树。
经分析,该算法的时间复杂度为O(n)。
4、清空二叉树算法思想:
将二叉树的清空过程写成函数,函数的参数同上,将根结点的左右孩子指针置为空,此时其左右子树的存储空间并未释放掉。
经分析,该算法的时间复杂度为O
(1)。
5、判定空二叉树的算法思想:
将判定空二叉树写成函数,对于一个二叉树,若根结点不存在则为空二叉树,否则不是空二叉树,那么只需要判断指向根结点的结构类型指针T是否为空即可。
经分析,该算法的时间复杂度为O(1