链表顺序表实验报告数据结构与算法分析.docx
《链表顺序表实验报告数据结构与算法分析.docx》由会员分享,可在线阅读,更多相关《链表顺序表实验报告数据结构与算法分析.docx(22页珍藏版)》请在冰豆网上搜索。
链表顺序表实验报告数据结构与算法分析
数据结构与算法分析课程设计报告
课题名称:
使用一个链表和顺序表构建城市数据库
提交文档组号:
2
编程学生姓名及学号:
测试学生姓名及学号:
报告学生姓名及学号:
指导教师姓名:
指导教师评阅成绩:
指导教师评阅意见:
.
.
提交报告时间:
2013年11月日
实验一:
Implementacitydatabaseusingunorderedlistsandlinklists
1.实验的目的和要求:
<1>采用C++的ASCII码文件和模块函数实现;
<2>熟练掌握数组列表和链表列表的实现;
<3>熟练掌握计算机系统的基本操作方法,了解如何编译、运行一个C++程序;
<4>上机调试程序,掌握查错、排错使程序能正确运行。
2.实验的环境:
1、硬件环境:
索尼笔记本电脑,Intel(R)Core(TM)i7-3632M,8GB内存可;
2、软件环境:
Windows8下的MicrosoftVisualStudio2008
2.算法描述:
数据结构与算法分析的背景:
数据结构是计算机程序设计的重要理论技术基础,它不仅是计算机学科的核心课称,而且已成为其他理工专业的热门选修课。
数据结构是一门专业选技术基础科。
一方面,它要求我们学会分析研究计算机加工的数据结构的特性,以便为应用涉及的数据选择适当的逻辑结构、存储结构及其相应的算法,并初步掌握算法的时间分析和空间分析的技术;另一方面,数据结构的学习过程也是复杂程序设计的训练过程,要求我们编写的程序结构清楚和正确易读,复合软件工程的规范,并培养我们的数据抽象能力。
本次课程设计就是对数据结构中的顺序表和链表的操作的应用。
顺序表:
1.顺序表的定义
(1)顺序存储方法
即把线性表的结点按逻辑次序依次存放在一组地址连续的存储单元里的方法。
(2)顺序表(SequentialList)
用顺序存储方法存储的线性表简称为顺序表(SequentialList)。
2.结点ai的存储地址
不失一般性,设线性表中所有结点的类型相同,则每个结点所占用存储空间大小亦相同。
假设表中每个结点占用c个存储单元,其中第一个单元的存储地址则是该结点的存储地址,并设表中开始结点a1的存储地址(简称为基地址)是LOC(a1),那么结点ai的存储地址LOC(ai)可通过下式计算:
LOC(ai)=LOC(a1)+(i-1)*c1≤i≤n
注意:
在顺序表中,每个结点ai的存储地址是该结点在表中的位置i的线性函数。
只要知道基地址和每个结点的大小,就可在相同时间内求出任一结点的存储地址。
是一种随机存取结构。
顺序表上实现的基本运算:
表的初始化;求表长;取表中第i个结点;查找值为x的结点;插入
(1)插入运算的逻辑描述
线性表的插入运算是指在表的第i(1≤i≤n+1)个位置上,插入一个新结点x,使长度为n的线性表:
(a1,…,ai-1,ai,…an)
变成长度为n+1的线性表:
(a1,…,ai-1,x,ai,…an)
注意:
①由于向量空间大小在声明时确定,当L->length≥ListSize时,表空间已满,不可再做插入操作;
②当插入位置i的值为i>n或i<1时为非法位置,不可做正常插入操作;
(2)顺序表插入操作过程
在顺序表中,结点的物理顺序必须和结点的逻辑顺序保持一致,因此必须将表中位置为n,n-1,…,i上的结点,依次后移到位置n+1,n,…,i+1上,空出第i个位置,然后在该位置上插入新结点x。
仅当插入位置i=n+1时,才无须移动结点,直接将x插入表的末尾。
(4)算法分析
①问题的规模
表的长度L->length(设值为n)是问题的规模。
②移动结点的次数由表长n和插入位置i决定
算法的时间主要花费在for循环中的结点后移语句上。
该语句的执行次数是n-i+1。
当i=n+1:
移动结点次数为0,即算法在最好时间复杂度是0
(1)
当i=1:
移动结点次数为n,即算法在最坏情况下时间复杂度是0(n)
③移动结点的平均次数Eis(n)
链表:
1:
一个单向链表的节点被分成两个部分。
第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址。
单向链表只可向一个方向遍历。
2:
链表最基本的结构是在每个节点保存数据和到下一个节点的地址,在最后一个节点保存一个特殊的结束标记,另外在一个固定的位置保存指向第一个节点的指针,有的时候也会同时储存指向最后一个节点的指针。
一般查找一个节点的时候需要从第一个节点开始每次访问下一个节点,一直访问到需要的位置。
但是也可以提前把一个节点的位置另外保存起来,然后直接访问。
当然如果只是访问数据就没必要了,不如在链表上储存指向实际数据的指针。
这样一般是为了访问链表中的下一个或者前一个(需要储存反向的指针)节点。
3:
在链表描述中,集合中的元素都放在链表的节点中进行描述。
链表中的节点不是一个数组元素,因此不能通过公式来映射定位某个元素。
取而代之的是,每个节点中都包含了下一个节点的位置信息,链表的表头包含了第一个节点的位置信息。
4.1:
为了在集合中找到第k个元素,就必须从表头开始,遍历第1个到第k个节点。
它的时间复杂度是O(k),平均时间复杂度为O(length)。
4.2:
为了在集合中删除第k个元素,就要先找到第k-1和第k个节点,使第k-1个节点的下一个节点位置信息指向第k+1个节点,然后释放第k个节点所占的空间。
它的时间复杂度是O(k),平均时间复杂度为O(length)。
4.3:
插入和删除的过程很相似,首先要创建一个新的节点,然后找到第k-1个节点,在该节点的后面插入新的节点,并把第k-1个节点、新的节点的下一个节点位置信息进行适当设置。
它的时间复杂度是O(k),平均时间复杂度为O(length)。
5:
采用数组描述方法的集合仅需要能够保存所有元素的空间以及保存集合最大尺寸所需要的空间。
链表描述需要除集合元素本身的空间意外,还需要额外的空间,用例保存链接节点指向下一个节点位置的指针。
但一般情况下,链表描述要比数值描述的空间利用率高得多。
6:
虽然数组描述、链表描述插入和删除操作的平均时间复杂度均为O(length),但由于移动元素的操作比遍历元素的操作的开销要大得多,所以采用链表描述所实现的插入和删除操作要比数组描述执行得更快。
而采用数组描述可以在常数时间内访问第k个元素,而在链表中,这种操作所需要的时间是O(k)。
●
程序流程图
3.源程序清单:
//顺序表实现城市数据库
#include
#include
#include"stdlib.h"
#include
#include
usingnamespacestd;
#defineLIST_INIT_SIZE100
#defineLISTINCREMENT10
#defineElemTypestring
typedefstruct
{
ElemTypem_Name;
intm_X;
intm_Y;
}CityData;
typedefstruct
{
CityData*elem;
intlength;//当前表长
intlistsize;//表总长,初始为100
}SqList;
voidInitList_Sq(SqList&L)
{
L.elem=newCityData[LIST_INIT_SIZE];
if(!
L.elem)
{
exit(0);
}
L.length=0;
L.listsize=LIST_INIT_SIZE;
}
voidDestroyList(SqList&L)
{
L.length=0;
L.listsize=0;
free(L.elem);
L.elem=NULL;
}
voidClearList(SqList&L)
{
L.length=0;
}
boolListEmpty(SqListL)
{
return(L.length==0)?
false:
true;
}
intListLength(SqListL)
{
returnL.length;
}
//获取第i个元素(从1开始计数,下同)
voidGetElem(SqListL,inti,CityData&e)
{
if(i<1||i>L.length)
{
cout<<"错误的位置!
"<return;
}
else
{
e=L.elem[i-1];
}
}
//查找节点e返回位置
intLocateElem(SqListL,CityDatae,bool(*compare)(CityData,CityData))
{
CityData*p;
p=L.elem;
inti=0;
while(i(compare(*p,e)))
{
p++;
i++;
}
if(i{
returni+1;
}
else
{
return0;
}
}
//在第i个元素后插入一个元素
voidListInsert_Sq(SqList&L,inti,CityDatae)
{
if(i>=1&&i<=L.length+1)
{
//如果存储空间不足,则以LISTINCREMENT为增量,扩展空间
if(L.length>=L.listsize)
{
CityData*base;
base=newCityData[L.listsize];
for(intcount=0;count{
base[count]=L.elem[count];
}
L.elem=newCityData[L.listsize+LISTINCREMENT];
for(intnum=0;num{
L.elem[num]=base[num];
}
delete[]base;
L.listsize+=LISTINCREMENT;
}
//将第i个元素之后的元素,依次向后移动一位,之后执行插入
CityData*q;
q=&(L.elem[i-1]);
CityData*p;
for(p=&(L.elem[L.length]);p>q;p--)
{
*p=*(p-1);
}
*q=e;
L.length++;
cout<<"插入成功"<return;
}
cout<<"插入位置非法!
"<}
//按名字删除元素,并由e返回其值
voidListDelete_Name(SqList&L,stringname,CityData&e)
{
if(L.length>0)
{
CityData*p=&(L.elem[0]);
intcount=0;
while(p->m_Name!
=name&&count{
count++;
p++;
}
//之后将此元素后的元素依次向前移动一位,下同
if(count{
e=*p;
CityData*q=L.elem+L.length-1;
for(;p<=q;p++)
{
*p=*(p+1);
}
L.length--;
cout<<"删除成功"<}
else
{
cout<<"数据库中没有该记录"<}
}
}
//按坐标删除元素,,并由e返回其值
voidListDelete_Coordinate(SqList&L,intX,intY,CityData&e)
{
if(L.length>0)
{
CityData*p=&(L.elem[0]);
intcount=0;
while(p->m_X!
=X&&p->m_Y!
=Y&&count{
count++;
p++;
}
if(count{
e=*p;
CityData*q=L.elem+L.length-1;
for(;p<=q;p++)
{
*p=*(p+1);
}
L.length--;
cout<<"删除成功"<}
else
{
cout<<"数据库中没有该记录"<}
}
}
//读取文件中城市信息,更新表(会清除原有表中数据)
voidListReadFile(SqList&L)
{
ClearList(L);
CityDatacitydata;
ifstreamifile;
ifile.open("City.txt",ios_base:
:
in);
while(ifile>>citydata.m_Name>>citydata.m_X>>citydata.m_Y)
{
ListInsert_Sq(L,1,citydata);
}
ifile.close();
cout<<"读取完毕"<}
//按名字检索城市
voidListSearch_Name(SqListL,stringname,CityData&e)
{
if(L.length>0)
{
CityData*p=L.elem;
intcount=0;
while(p->m_Name!
=name&&count{
count++;
p++;
}
if(count{
e=*p;
cout<<"城市名:
"<m_Name<cout<<"坐标X:
"<m_X<cout<<"坐标Y:
"<m_Y<}
else
{
cout<<"数据库中没有这个记录"<}
}
}
//按坐标检索城市
voidListSearch_Coordinate(SqListL,intX,intY,CityData&e)
{
if(L.length>0)
{
CityData*p=L.elem;
intcount=0;
while(p->m_X!
=X&&p->m_Y!
=Y&&count{
count++;
p++;
}
if(count{
e=*p;
cout<<"城市名:
"<m_Name<cout<<"坐标X:
"<m_X<cout<<"坐标Y:
"<m_Y<}
else
{
cout<<"数据库中没有这个记录"<}
}
}
//打印到指定点距离小于distance的城市
voidPrint(SqListL,intY,intX,intdistance)
{
if(L.length==0)
{
cout<<"空,你还没有读取记录,或者记录为空"<return;
}
for(inti=0;i{
intx=(L.elem[i].m_X>=X)?
L.elem[i].m_X-X:
X-L.elem[i].m_X;
inty=(L.elem[i].m_Y>=Y)?
L.elem[i].m_Y-Y:
Y-L.elem[i].m_Y;
intDistance=x*x+y*y;
intd=distance*distance;
if(Distance<=d)
{
cout<<"城市名:
"<cout<<"坐标X:
"<cout<<"坐标Y:
"<}
i++;
}
}
//将表中城市信息保存到文件中(会清除文件中原有数据)
voidPreserve(SqListL)
{
ofstreamofile;
ofile.open("City.txt",ios_base:
:
out);
for(inti=0;i{
ofile<}
ofile.close();
cout<<"存档成功!
"<}
//打印出所有节点
voidListTraverse(SqListL)
{
if(L.length==0)
{
cout<<"空,你还没有读取记录,或者记录为空"<}
else
{
for(inti=0;i{
4.运行结果:
对于需要比较不同算法性能优劣的题目,应设计并填写一张性能比较表格,列出不同算法在同一指标下的性能表现。
但仅仅罗列出一堆数据是不够的,还应将数字转化为图象、曲线等,帮助读者更直观地理解测试结果。
对于需要利用某算法解决某问题的题目,应设计并填写一张测试用例表。
每个测试用例至少应包括下列内容:
●测试输入:
设计一组输入数据;
●测试目的:
设计该输入的目的在于测试程序在哪方面可能存在的漏洞;
●正确输出:
对应该输入,若程序正确,应该输出的内容;
●实际输出:
该数据输入后,实际测试得到的输出内容;
●错误原因:
如果实际输出与正确输出不符,需分析产生错误的可能原因;
●当前状态:
分为“通过”(实际输出与正确输出相符)、“已改正”(实际输出与正确输出不符,但现在已修改正确)、“待修改”(实际输出与正确输出不符,且尚未改正)三种状态。
(举例):
序号
功能
名称
测试项
描述/输入/操作
期望结果
真实结果
备注
1
插入一个城市的数据
能否插入并保存一个城市的数据
输入1,根据提示输入一个城市的名称,x坐标,y坐标,如重庆,1,1
能够输入一个城市的名称,x坐标,y坐标并保存
城市名称和x,y坐标能输入并能够保存
通过
2
按名字删除城市的数据
按照城市的名字删除城市的所有数据
输入2,根据提示输入城市名称,如重庆
删除这个城市名称的所有数据,同时会计算出算法的时间
删除了这个城市名称的所有数据,计算出了算法的时间
通过
3
按坐标删除城市的数据
按照城市的坐标删除该城市的所有数据
输入3,根据提示输入城市的x,y坐标,如1,1
删除了该x,y坐标的城市的所有数据,计算出算法的时间
删除了该x,y坐标的城市的所有数据,计算出了算法的时间
通过
4
按名字查找城市的数据
按城市名称查找一个城市,并显示该城市的相关信息
输入4,根据提示输入城市名称,如重庆
显示关于这个城市的相关信息,计算出算法的时间
显示了关于这个城市的相关信息,计算出了算法的时间
通过
5
按坐标查找城市的数据
按城市坐标查找一个城市,并显示该城市的相关信息
输入5,根据提示输入城市坐标,如输入1,1
显示关于这个城市的相关信息,计算出算法的时间
显示了关于这个城市的相关信息,计算出了算法的时间
通过
6
按指定点距离内显示记录
按照规定的距离内显示该距离内的数据
输入6,根据提示输入距离5
显示该距离内的数据
显示了该距离内的数据
通过
7
显示所有城市的数据
显示当前所有城市的数据
输入7
显示当前所有城市的数据
显示了当前所有城市的数据
通过
8
读取已保存的城市的数据
读取已经存档的所有城市的数据,即刷新表中的数据
输入8
读取了存档的所有数据
读取了存档的所有数据
通过
9
保存
存储数据
输入9
存储所有所有数据
存储了所有数据
通过
0
随机生成
随机生成城市名称和坐标
先输入0,再根据要求输入产生的长度
会随机生成相应长度的数据
输入7,会显示出随机产生的相应长度的数据
通过
测试结论
各项功能已经基本实现,但其中有些部分太过繁杂,可以简化一些,尤其是进行下一项操作时,前面的操作会消失,这会带来许多不便,需要改进
备注:
分为“通过”(实际输出与正确输出相符)、“已改正”(实际输出与正确输出不符,但现在已修改正确)、“待修改”(实际输出与正确输出不符,且尚未改正)三种状态。
可在此说明错误原因
这一部分需根据题目类型设计提供相应的测试方法和结果(请勿粘贴测试结果图)。
5.实验运行情况分析(包括算法、运行结果、运行环境等问题的总体讨论)。
算法分析比较:
顺序表和链表的使用各有优劣。
顺序表需要提前估计线性表的大小并且插入删除效率低需要移动大量结点,优点在于表中节点没有浪费存储空间,并且可以按位置随机访问,存储速度快;
链表优点在于插入删除效率高,无需提前估计表的大小(大小无需固定),表中元素个数没有限制,缺点在于访问结点需要从表头遍历查找并且每个节点除了储存元素还需要附加空间存储指针。
顺序表和链表时间性能比较:
像取出线性表中第i个元素这样的按位置随机访问的操作,使用顺序表更快一些;取前趋和后继结点的操作在顺序表中可以很容易调整当前的位置向前或向后,因此这两种操作的时间为O
(1);
相比之下,单链表不能直接访问上述的元素,按位置访问只能从表头开始,直到找到那个特定的位置,所需要的平均时间为O(n)。
给出指向链表中某个合适位置的指针后,插入和删除操作所需的时间仅为O(