数据结构课程实验报告7.docx
《数据结构课程实验报告7.docx》由会员分享,可在线阅读,更多相关《数据结构课程实验报告7.docx(54页珍藏版)》请在冰豆网上搜索。
数据结构课程实验报告7
课程实验报告
课程名称:
数据结构
专业班级:
信安
学号:
姓名:
指导教师:
报告日期:
2015.4.5
计算机科学与技术学院
1课程实验概述
上机实验是对学生的一种全面综合训练,是与课堂听课、自学和练习相辅相成的必不可少的一个教学环节。
实验目的着眼于原理与应用的结合,使学生学会如何把书上的知识用语解决实际问题,能够理解和运用常用的数据结构,如线性表、栈、队列、树、图、查找表等,并在此基础上建立相应的算法;通过上机实验使学生了解算法和程序的区别,培养学生把算法转换为程序的能力,提高学生解决实际问题的能力;学会分析研究计算机加工的数据结构的特性,以便为应用涉及的数据选择适当的逻辑结构、存储结构及其相应的算法,并初步掌握算法的时间分析和空间分析的技术。
2实验一基于顺序结构的线性表实现
2.1问题描述
编写一个程序,实现顺序表的各种基本运算,并在此基础上完成以下功能:
1) 初始化顺序表;
2) 释放顺序表;
3) 判断顺序表L是否为空;
4) 输出顺序表L的长度;
5) 输出顺序表L的第i个元素;
6) 输出元素e的位置;
7) 输出元素e的前一个元素;
8) 输出元素e的后一个元素;
9) 在第i个元素位置上插入f元素;
10) 删除L的第i个元素;
11) 输出顺序表L;
12) 保存顺序表L的数据。
2.2系统设计
1、数据类型
顺序表:
typedefstruct
{
ElemType*elem;//线性表首地址
intlength;//线性表当前长度
intlistsize;//线性表最大长度
}SqList;
数据类型:
int(可以在头文件中更改数据类型)
输入形式:
文件读取、键盘输入
输入范围:
-2^15~2^16
2、函数返回状态
判断为真:
TRUE0
判断为假:
FALSE-1
函数正确执行:
OK-2
函数执行错误:
ERROR-3
元素不存在:
NOTEXIST-4
内存分配溢出:
OVERFLOW-5
3、函数设计
InitList(&L)
操作前提:
L是一个未初始化的线性表。
操作结果:
将L初始化为一个空的线性表。
DestroyList(&L)
操作前提:
线性表L已存在。
操作结果:
销毁线性表L。
ListEmpty(L)
操作前提:
线性表L已存在。
操作结果:
若L为空表,则返回TURE,否则返回FALSE。
ListLength(L)
操作前提:
线性表L已存在。
操作结果:
返回L中数据元素个数。
GetElem(L, i,&e)
操作前提:
线性表L已存在,1≤i≤ListLength(L)。
操作结果:
用e返回L中第i个元素的值。
LocateElem (L,e)
操作前提:
线性表L已存在。
操作结果:
返回L中第一个e的位序。
若这样的数据元素不存在,则返回值为0。
PriorElem (L,e,&proi)
操作前提:
线性表L已存在。
操作结果:
返回L中第一个e的前一个数据元素。
若这样的数据元素不存在,则返回值为0。
NextElem (L,e,&next)
操作前提:
线性表L已存在。
操作结果:
返回L中第一个e的后一个的数据元素。
若这样的数据元素不存在,则返回值为0。
ListInsert (&L,i ,e)
操作前提:
线性表L已存在,1≤i≤ListLength(L)+1。
操作结果:
在L中第i个位置之前插入新的数据元素e,L的长度+1。
ListDelete (&L,i, e)
操作前提:
线性表L已存在且非空,1≤i≤ListLength(L)。
操作结果:
删除L的第i个元素数据,并用e返回其值,L的长度—1。
DisplayList(L)
操作前提:
线性表L已存在。
操作结果:
打印表中元素。
SaveList(L)
操作前提:
线性表L已存在。
操作结果:
保存表中元素。
4、主要函数流程图
ProiElem:
这里的查找元素操作可直接调用LocateElem函数返回元素的位置,NextElem函数的流程图与此相似,这里不在赘述)
ListInsert:
这个函数最主要的部分就是检查参数与元素的移位操作,若参数不合法(传入空线性表、插入位置在线性表之外)极易出现访问数组外元素问题;元素移位操作要在不丢失数据的前提下(从最后一个元素开始)进行。
ListDelete
删除函数同样最主要的部分就是检查参数与元素的移位操作,若参数不合法(传入空线性表、删除位置在线性表之外)极易出现访问数组外元素问题;元素移位操作要在不丢失数据的前提下(从被删除的元素开始)进行。
2.3系统实现
这里也只给出主要函数的代码,完整代码会在附录给出
函数名:
LocateElem
参数:
线性表L、要查找的元素
返回值:
若元素存在,返回位置,否则返回错误信息
函数功能:
获取元素的位置
statusLocateElem(SqListL,ElemTypee)
{
if(!
L.elem)
returnERROR;
for(inti=1;i{
if(e==L.elem[i])
returni;
}
returnNOTEXIST;
}
函数名:
PriorElem
参数:
线性表L、查找的元素、带回元素值的形参
返回值:
函数执行情况成功为OK,否则ERROR
函数功能:
获取元素的前继节点元素
statusPriorElem(SqListL,ElemTypecur,ElemType&pre_e)
{
/*不可获取线性表长度之外的元素,第0号元素不用*/
if(!
L.elem)
returnERROR;
intpos;
pos=LocateElem(L,cur);
if(pos<1)
returnERROR;
if(pos==1)
returnNOTEXIST;
pre_e=L.elem[pos-1];
returnOK;
}
函数名:
ListInsert
参数:
线性表L(引用)、要插入的位置及元素
返回值:
是否插入成功
函数功能:
插入元素到指定位置
statusListInsert(SqList&L,inti,ElemTypee)
{
/*不可获取线性表长度之外的元素,第0号元素不用*/
if(!
L.elem||i>L.length+1||i<1)
returnERROR;
if(L.length==L.listsize)
returnOVERFLOW;
for(intj=L.length+1;j>i;j--)
{
L.elem[j]=L.elem[j-1];
}
L.elem[i]=e;
L.length++;
returnOK;
}
函数名:
ListDelete
参数:
线性表L(引用)、位置、带回被删除的元素e
返回值:
是否删除元素成功
函数功能:
删除指定位置的元素并带回元素
statusListDelete(SqList&L,inti,ElemType&e)
{
/*不可获取线性表长度之外的元素,第0号元素不用*/
if(!
L.elem||i>L.length||i<1)
returnERROR;
e=L.elem[i];
for(intj=i;j{
L.elem[j]=L.elem[j+1];
}
L.length--;
returnOK;
}
调试分析
(1) 调试过程中主要遇到哪些问题?
是如何解决的?
调式过程中发现总是粗心大意忘了加分号或者打错字,所以学习C语言一定要细心仔细,不能着急。
用DisplayList函数时出现了问题,后来经过询问同学和查阅书本对程序进行了改正,使其能够正确进行。
(2) 经验和体会
通过这次上机实验,对顺序表的各功能有了进一步的了解和学习,也对C语知识进行了巩固。
实验结果截图
实验测试数据为:
1、2、3、4、5、6、7、8、9。
这里依然只给出主要函数结果。
1)查找元素位置:
2)查找前继元素:
3)插入元素:
4)删除元素:
5)遍历线性表:
2.4效率分析
由于线性表具有随机访问的特性,所以线性表操作的时间基本来自于线性表的遍历、插入及删除操作。
假定线性表中有n个元素。
1)获取元素位置:
最好的情况:
第一个元素即为所要查找的元素,执行1次操作;
最坏的情况:
最后一个元素为要查找的元素,执行n次操作;
若所有元素访问的概率相等,则要执行(n+1)/2次操作。
总体来说时间复杂度为O(n)。
2)插入元素:
最好的情况:
插入到线性表尾,执行1次操作;
最坏的情况:
插入到线性表头,执行n次操作;
若所有元素访问的概率相等,则要执行(n+1)/2次操作。
总体来说时间复杂度为O(n)。
3)删除元素:
最好的情况:
删除线性表尾元素,执行1次操作;
最坏的情况:
删除线性表头元素,执行n次操作;
若所有元素访问的概率相等,则要执行(n+1)/2次操作。
总体来说时间复杂度为O(n)。
结果分析:
线性表比较适合经常访问但修改较少的实际运用。
3实验二基于链式结构的线性表实现
3.1问题描述
编写一个程序,实现线性链表的各种基本运算,并在此基础上完成以下功能:
1) 初始化线性链表;
2) 释放线性链表;
3) 判断线性链表L是否为空;
5) 输出线性链表L的长度;
5) 输出线性链表L的第i个元素;
6) 输出元素e的位置;
7) 输出元素e的前一个元素;
8) 输出元素e的后一个元素;
9) 在第i个元素位置上插入f元素;
10) 删除L的第i个元素;
11) 输出线性链表L;
12) 保存线性链表L的数据。
3.2系统设计
1、数据类型
线性链表:
structLinkList
{
ElemTypedata;//链表元素
LinkList*next;//指向后继节点的指针
}
数据类型:
int(可以在头文件中更改数据类型)
输入形式:
文件读取、键盘输入
输入范围:
-2^15~2^16
2、函数返回状态
判断为真:
TRUE0
判断为假:
FALSE-1
函数正确执行:
OK-2
函数执行错误:
ERROR-3
元素不存在:
NOTEXIST-4
内存分配溢出:
OVERFLOW-5
3、函数设计
InitList(&L)
操作前提:
L是一个未初始化的线性表。
操作结果:
将L初始化为一个空的线性表。
DestroyList(&L)
操作前提:
线性表L已存在。
操作结果:
销毁线性表L。
ListEmpty(L)
操作前提:
线性表L已存在。
操作结果:
若L为空表,则返回TURE,否则返回FALSE。
ListLength(L)
操作前提:
线性表L已存在。
操作结果:
返回L中数据元素个数。
GetElem(L, i,&e)
操作前提:
线性表L已存在,1≤i≤ListLength(L)。
操作结果:
用e返回L中第i个元素的值。
LocateElem (L,e)
操作前提:
线性表L已存在。
操作结果:
返回L中第一个e的位序。
若这样的数据元素不存在,则返回值为0。
PriorElem (L,e,&proi)
操作前提:
线性表L已存在。
操作结果:
返回L中第一个e的前一个数据元素。
若这样的数据元素不存在,则返回值为0。
NextElem (L,e,&next)
操作前提:
线性表L已存在。
操作结果:
返回L中第一个e的后一个的数据元素。
若这样的数据元素不存在,则返回值为0。
ListInsert (&L,i ,e)
操作前提:
线性表L已存在,1≤i≤ListLength(L)+1。
操作结果:
在L中第i个位置之前插入新的数据元素e,L的长度+1。
ListDelete (&L,i, e)
操作前提:
线性表L已存在且非空,1≤i≤ListLength(L)。
操作结果:
删除L的第i个元素数据,并用e返回其值,L的长度—1。
DisplayList(L)
操作前提:
线性表L已存在。
操作结果:
打印表中元素。
SaveList(L)
操作前提:
线性表L已存在。
操作结果:
保存表中元素。
4、主要函数流程图
NextElem:
这里的查找元素操作可直接调用LocateElem函数返回元素的位置,ProiElem函数的流程图与此相似,这里不在赘述)
ListInsert:
这个函数最主要的部分就是检查参数与节点的插入操作,若参数不合法(传入空线性链表、插入位置在线性链表之外)极易出现访问链表外元素问题;若插入首节点,要注意修改头指针,插入其他节点注意不要丢失指针,应先保存后继节点指针后,再修改前驱节点的后继。
ListDelete
删除函数同样最主要的部分就是检查参数与节点的删除操作,若参数不合法(传入空线性链表、删除位置在线性链表之外)极易出现访问链表外元素问题;若删除首节点,要先保存首节点的后继节点,再释放首地址,再修改头指针;删除其他节点要注意查找到它的前继节点,再修改前继节点的后继,释放节点。
3.3系统实现
这里也只给出主要函数的代码,完整代码会在附录给出
函数名:
LocateElem
参数:
线性表L、要查找的元素
返回值:
若元素存在,返回位置,否则返回错误信息
函数功能:
获取元素的位置
statusLink:
:
LocateElem(LinkL,ElemTypee)
{
if(L.head==NULL)
returnERROR;
intloc=0;
L.cur_p=head;
while(L.cur_p)
{
loc++;
if(L.cur_p->data==e)
returnloc;
L.cur_p=L.cur_p->next;
}
returnNOTEXIST;
}
函数名:
NextElem
参数:
线性表L、查找的元素、带回元素值的形参
返回值:
函数执行情况成功为OK,否则ERROR
函数功能:
获取元素的后继节点元素
statusLink:
:
NextElem(LinkL,ElemTypecur,ElemType&e)
{
if(L.head==NULL)
returnERROR;
intpos;
pos=LocateElem(L,cur);
if(pos<1)
returnERROR;
if(pos==L.length)
returnNOTEXIST;
GetElem(L,pos+1,e);
returnOK;
}
函数名:
ListInsert
参数:
线性表L(引用)、要插入的位置及元素
返回值:
是否插入成功
函数功能:
插入元素到指定位置
statusLink:
:
ListInsert(Link&L,inti,ElemTypee)
{
if(i<1||i>L.length+1)
returnERROR;
if(!
L.head)
{
L.head=(LinkList*)malloc(sizeof(LinkList));
if(!
L.head)
returnOVERFLOW;
L.head->data=e;
L.head->next=NULL;
L.length=1;
returnOK;
}
L.cur_p=L.head;
for(intj=1;j{
L.cur_p=L.cur_p->next;
if(!
L.cur_p)
returnERROR;
}
L.tail=(LinkList*)malloc(sizeof(LinkList));
if(!
L.tail)
returnOVERFLOW;
L.tail->data=e;
if(i==1)
{
L.tail->next=L.head;
L.head=L.tail;
}
else
{
L.tail->next=L.cur_p->next;
L.cur_p->next=L.tail;
}
L.length++;
returnOK;
}
函数名:
ListDelete
参数:
线性表L(引用)、位置、带回被删除的元素e
返回值:
是否删除元素成功
函数功能:
删除指定位置的元素并带回元素
statusLink:
:
ListDelete(Link&L,inti,ElemType&e)
{
if(!
L.head||i<1||i>L.length)
returnERROR;
L.cur_p=L.head;
if(i==1)
{
L.head=L.head->next;
free(L.cur_p);
L.length--;
returnOK;
}
for(intj=1;j{
L.cur_p=L.cur_p->next;
if(!
L.cur_p)
returnERROR;
}
L.tail=L.cur_p->next;
L.cur_p->next=L.cur_p->next->next;
free(L.tail);
L.tail=NULL;
L.length--;
returnOK;
}
调试分析
(1) 调试过程中主要遇到哪些问题?
是如何解决的?
调式过程中发现总是无法插入到首节点,后来发现是没单独考虑首节点的插入操作。
用删除t函数时若删除第一个元素就出现异常退出,经调试发现操作的空指针,是未考虑删除首节点导致的。
(2) 经验和体会
通过这次上机实验,对顺序链表的各功能有了进一步的了解和学习,也对C语知识进行了巩固。
实验结果截图
实验数据:
1、2、3、4、5、6、7、8、9
1)获取元素位置:
2)获取后继元素:
3)插入元素:
4)删除元素:
5)遍历线性链表:
3.4效率分析
由于线性链表不具有随机访问的特性,所以线性表操作的时间都来自于线性表的遍历操作。
假定线性表中有n个元素。
1)获取元素位置:
最好的情况:
第一个元素即为所要查找的元素,执行1次操作;
最坏的情况:
最后一个元素为要查找的元素,执行n次操作;
若所有元素访问的概率相等,则要执行(n+1)/2次操作。
总体来说时间复杂度为O(n)。
链式结构使得插入,删除操作不需要移动其他元素,只要更改指针指向,所以线性链表比较适合需要经常改动数据的结构,但同时也有一个问题,链表的空间使用效率不是很高,而且一个节点内存储的数据越少,利用率越低。
4实验三基于二叉链表的二叉树实现
4.1问题描述
编写一个程序,实现二叉树的各种基本运算,并在此基础上完成以下功能:
1) 初始化二叉树;
2) 释放二叉树;
3) 判断二叉树是否为空;
6) 输出二叉树的深度;
5) 先序遍历二叉树;
6) 中序遍历二叉树;
7) 后序遍历二叉树;
8) 层序遍历二叉树;
9) 在二叉树上插入f元素;
10) 删除二叉树的元素e;
11) 保存二叉树的数据。
4.2系统设计
二叉树节点的定义
structBiTreeNode
{
ElemTypedata;//节点元素
BiTreeNode*parents;
BiTreeNode*Lson;//指向左后继节点的指针
BiTreeNode*Rson;//指向右后继节点的指针
};
队列,层序遍历二叉树时使用
structQueue
{
BiTreeNode*node;//保存二叉树节点
Queue*next;
};
1、数据类型
数据类型:
int(可以在头文件中更改数据类型)
输入形式:
文件读取、键盘输入
输入范围:
-2^15~2^16
2、函数返回状态
判断为真:
TRUE0
判断为假:
FALSE-1
函数正确执行:
OK-2
函数执行错误:
ERROR-3
元素不存在:
NOTEXIST-4
内存分配溢出:
OVERFLOW-5
3、函数设计
注:
所有函数在BiTree类中实现,所以函数没传根指针
InitBiTree()
操作前提:
B是一个未初始化的二叉树。
操作结果:
将B初始化为一个空的二叉树。
DestroyBiTree()
操作前提:
二叉树B已存在。
操作结果:
销毁二叉树B。
BiTreeEmpty()
操作前提:
二叉树B已存在。
操作结果:
若B为空树,则返回TURE,否则返回FABSE。
BiTreeDepth()
操作前提:
二叉树B已存在。
操作结果:
返回B的深度。
GetRoot()
操作前提:
二叉树B已存在。
操作结果:
返回二叉树的根指针。
Value (e)
操作前提:
二叉树B已存在,e是二叉树中的某个节点。
操作结果:
返回节点e的数据。
Assign (&e,valuei)
操作前提:
二叉树B已存在,e是B中的一个节点。
操作结果:
节点e的值改为value。
Parent(e)
操作前提:
二叉树B已存在,e是B中的一个节点。
操作结果:
返回e的双亲节点。
LeftChild(e)
操作前提:
二叉树B已存在,e是B中的一个节点。
操作结果:
返回e的左子树根节点。
RightChild(e)
操作前提:
二叉树B已存在,e是B中的一个节点。
操作结果:
返回e的右子树根节点。
LeftSibling(e)
操作前提:
二叉树B已存在,e是B中的一个节点。
操作结果:
返回e的左兄弟节点,若e为B左子树,则返回空。
RightSibling(e)
操作前提:
二叉树B已存在,e是B中的一个节点。
操作结果:
返回e的右兄弟节点,若e为B右子树,则返回空。
BiTreeInsert (e)
操作前提:
二叉树B已存在。
操作结果:
在B中插入新的数据元素e。
BiTreeDeBete (e)
操作前提:
二叉树B已存在且非空。
操作结果:
删除B的元素数据e。
PreOrderTraverse()
操作前提:
二叉树B已存在。
操作结果:
先序遍历二叉树