数据结构第二次试验.地铁换乘.docx

上传人:聆听****声音 文档编号:703898 上传时间:2023-04-29 格式:DOCX 页数:13 大小:683.28KB
下载 相关 举报
数据结构第二次试验.地铁换乘.docx_第1页
第1页 / 共13页
数据结构第二次试验.地铁换乘.docx_第2页
第2页 / 共13页
数据结构第二次试验.地铁换乘.docx_第3页
第3页 / 共13页
数据结构第二次试验.地铁换乘.docx_第4页
第4页 / 共13页
数据结构第二次试验.地铁换乘.docx_第5页
第5页 / 共13页
数据结构第二次试验.地铁换乘.docx_第6页
第6页 / 共13页
数据结构第二次试验.地铁换乘.docx_第7页
第7页 / 共13页
数据结构第二次试验.地铁换乘.docx_第8页
第8页 / 共13页
数据结构第二次试验.地铁换乘.docx_第9页
第9页 / 共13页
数据结构第二次试验.地铁换乘.docx_第10页
第10页 / 共13页
数据结构第二次试验.地铁换乘.docx_第11页
第11页 / 共13页
数据结构第二次试验.地铁换乘.docx_第12页
第12页 / 共13页
数据结构第二次试验.地铁换乘.docx_第13页
第13页 / 共13页
亲,该文档总共13页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

数据结构第二次试验.地铁换乘.docx

《数据结构第二次试验.地铁换乘.docx》由会员分享,可在线阅读,更多相关《数据结构第二次试验.地铁换乘.docx(13页珍藏版)》请在冰点文库上搜索。

数据结构第二次试验.地铁换乘.docx

数据结构实验报告

(二)

学院自动化学院学号08016332

姓名徐璐峰

日期2017-12-18

实验目的

1)熟练掌握图的存储方式;

2)了解图的特性,学习在实际问题背景下灵活运用图;

3)掌握图的两种最短路径算法。

实验内容

为简化问题,假设南京现有三条地铁线:

1号线、2号线和3号线,线路都是双向的。

3条地铁线的站点名分别如下,地铁线交叉的换乘点用T1、T2等表示。

请根据3条地铁线的站点和换乘点构造图。

编写程序,任意输入两个站名名称,输出乘坐地铁最少需要经过的车站数量(含输入的起点和终点,换乘站点只计算一次)。

地铁1号线(直线)经过车站:

A1A2A3T1A4A5A6A7A8T2A9A10A11A12T3A13A14A15T4A16

地铁2号线(直线)经过车站:

B1T5B2B3B4B5T2B6B7B8B9B10B11T3B12B13T6B14B15

地铁3号线(环线)经过车站:

C1C2C3C4C5T1C6C7C8C9C10T5C11C12C13T6C14C15T4C16C17C18

实验要求

1)用户从键盘输入两个不同的站名,程序输出最少需要经过的车站数量(含输入的起点和终点,换乘站点只计算一次);

2)分别基于迪杰斯特拉算法和弗洛里德算法实现上述地铁换乘问题;

3)程序功能模块的划分要适当,多使用流程图来描述算法结构。

1需求分析

1)输入的形式和输入值的范围。

输入的形式需要是地铁站的名称,如“A1”、“B7”、

“C14”。

所输入的的站点名不能是所要求的站点名之外的名称。

2)输出的形式。

所输出的是基于弗洛里德算法与迪杰斯特拉算法进行的最短路径长度求解的结果,以及最短路径的车站路径编号,并在输入错误的时候允许重复输入。

3)程序所能达到的功能。

用户输入车站起点与车站终点之后,通过迪杰斯特拉算法与

弗洛伊德算法,输出从起点到终点的最短路径长度,以及最短路径所经过的车站编号。

4)测试数据。

在程序运行的开始,会提示用户输入起点与终点,在用户输入起点与终点之后,程序会输出根据弗洛伊德算法所得到的最短路径所经过的车站数量,及经过的车站路径的编号。

之后会再输出根据迪杰斯特拉算法所得的最短路径所经过的车站数量,及所经过的车站路径的编号。

如果所输入起点或终点不存在,则会提示“起点/终点输入错误,请确认后重新输入。

”之后会继续出现“请输入起点/终点:

”之后,用户就可以重新输入原来输入错误的起点或终点车站名。

在确认起点与终点都输入正确之后,程序就会输出最短路径与路径编号。

在一次程序执行完毕后,支持用户重复输入。

2概要设计

在程序中之定义了一种抽象数据类型,structGraph,它包含三个元素,intarrAcc[56][56];//邻接矩阵intverCount;//点数intarcCount;//边数。

并且在程序开始的时候,还定义了长度为56的string型数组,用于存储所有的地铁站点。

在程序运行开始的时候,会先调用change_train函数,在change_train函数中,会先对g.arrAcc[56][56]邻接矩阵进行初始化。

之后调用floyd函数,及printres函数,输出起点到终点的最短路径长度及路径车站编号。

之后,会继续调用Dijkstra函数及searchPath函数,输出起点到终点的最短路径长度及路径车站编号。

3详细设计

类视图

整个程序中所写的函数有:

floyd函数,transform函数,printres函数,Dijkstra函数,

searchPath函数,change_train函数。

Floyd函数(图g,邻接矩阵dis[][],用来存储路径的矩阵path[][])

{

//初始化path矩阵

For(row0to总的站点数)

For(col0to总的站点数)path[row][col]←row;

For(k0to总的站点数)For(i0to总的站点数)

For(j0to总的站点数)

{

//存在更近的路径,更新

If(dis[i][j]>dis[i][k]+dis[k][j]){

dis[i][j]←dis[i][k]+dis[k][j];path[i][j]←path[k][j];

}

}

}

Transform函数(字符c)

{

For(i0to站点数)

If(存储所有站点的数组s[i]==c)

返回i;

}

Printres函数(图Graph*p,经floyd函数转化后的邻接矩阵dis[56][56],存储路径的矩阵

path[56][56],起点start,终点end)

{

起点数码st=transform(起点字符start);

终点数码en=transform(终点字符end);//将起点与终点转化为存储站点的数组中对应的下标输出<<”起点->终点”<

输出<<"根据弗洛伊德算法得,最少经过的车站数量:

"<

输出<<"经过的车站路径编号:

";For(i0to站点数)

For(j0to站点数)

{

If(i==起点数码st并且j==终点数码en)//输出路径

{

stackpathrout;//压栈

k←j;do{

k←path[i][k];

k入pathrout的栈

}当(k不等于i);

输出<<存储站点的数组s[pathrout的栈顶元素];弹出pathrout的栈顶元素;

length←栈pathrout的大小;For(t0tolength)

{

输出<<"->"<<存储站点的数组s[pathrout的栈顶元素];弹出pathrout的栈顶元素;

}

输出<<"->"<<用户输入的终点;

跳出循环;

}

}

}

Dijkstra函数(总的站点数n,起点v,当前点到起点的最短路径长度distence,记录当前节点的掐一个节点prev,记录图两点间的路径长度c[56][56])

{

bools[56];//判断是否已存入该点到S集合中For(i0to总的站点数n)

{

distence[i]←c[v][i];

s[i]=0;//初始都未用过该点

If(起点到第i个点的距离为1000)

第i个点的前一个点为0;

否则

第i个点的前一个点为起点;

}

//依次将未放入S集合的结点中,取distence[]最小值的结点,放入结合S中

//一旦S包含了所有V中顶点,distence就记录了从源点到所有其他顶点之间的最短路径长度For(i1ton)

{

u←v;

//找出当前未使用的点j的distence[j]最小值For(j0ton)

if(s[j]等于0并且起点到第j个点的距离(temp)小于1000)

{

u←j;//u保存当前邻接点中距离最小的点的号码

tmp←distence[j];

}

s[u]←1;//表示u点已存入S集合中

//更新distenceFor(j0ton)

If(s[j]为0并且点j到点u的距离c[u][j]小于1000)

{

newdist←distence[u]+c[u][j];If(newdist

{

distence[j]←newdist;//更新起点到第j个点的最短路径

prev[j]←u;//更新第j个点的前一个点

}

}

}

}

searchPath函数(用于表示当前点的前一个点的数组prev,起点v,intu)

{

intque[56];tot←1;que[tot]←u;

tot自增;tmp←prev[u];当(tmp不等于v)

{

que[tot]←tmp;

tot自增;tmp←prev[tmp];

}

que[tot]←v;

For(itotto1)If(i不等于1)

输出<";

否则

换行;

}



输出<

change_train函数()

{

distence[56];记录起点到当前点的最短路径

prev[56];记录当前点的前一个结点

path[56][56];记录路径

matrix[56][56];用于备份的矩阵

arrAcc[56][56];邻接矩阵

g.verCount=56;//定义总的站点的数目+1For(i0to56)

distence[i]←max;

//初始化邻接矩阵

For(i0tog.verCount(56)){For(k0tog.verCount(56))

{

If(i与k相同)

g.arrAcc[i][k]=0;

否则

g.arrAcc[i][k]=max;

}

}

//输入A环线各点连接情况,每个边权重都为1

a[20]={0,1,2,49,3,4,5,6,7,50,8,9,10,11,51,12,13,14,52,15};

For(i0to19)

{

g.arrAcc[a[i]][a[i+1]]←1;

g.arrAcc[a[i+1]][a[i]]←1;

}

//输入B线各点连接情况,每个权重都为1

b[19]={16,53,17,18,19,20,50,21,22,23,24,25,26,51,27,28,54,29,30};//19个点

For(i0to18)

{

g.arrAcc[b[i]][b[i+1]]←1;

g.arrAcc[b[i+1]][b[i]]←1;

}

//输入C线各点连接情况,每个权重都为1

c[23]={31,32,33,34,35,49,36,37,38,39,40,53,41,42,43,54,44,45,52,46,47,48,31};//23

个点(因为是环线,所以点数加一)

For(i0to22)

{

g.arrAcc[c[i]][c[i+1]]←1;

g.arrAcc[c[i+1]][c[i]]←1;

}

For(i0to56){For(j0to56)

{

arrAcc[i][j]←g.arrAcc[i][j];

}

}

//因为使用完弗洛伊德算法之后,g.arrAcc的返回值已经变化了,

//故在此将g.arrAcc中的值都赋值给matrix二维数组中,进行备份

For(i0to56){For(j0to56)

{

matrix[i][j]←g.arrAcc[i][j];

}

}

For(i0to56){matrix[i][i]←max;

}

while(true){//利用这个死循环来实现用户的重复输入调用floyd函数(&g,arrAcc,path);

定义judge1=0,judge2=0;输出<<"请输入起点与终点:

";输入>>start>>end;

当(judge1==0或者judge2==0){For(i0to56){

If(s[i]==start){judge1←1;跳出循环;}

否则judge1←0;

}

If(judge1==0){

输出<<"起点输入错误,请确认后重新输入!

"<<换行;

输出<<"请输入起点:

";输入>>start;

}

For(i0to56){

If(s[i]==end){judge2←1;跳出循环;}否则judge2←0;

}

If(judge2==0){

输出<<"终点输入错误,请确认后重新输入!

"<<换行;

输出<<"请输入终点:

";输入>>end;

}

}

进行重置

n=transform(end);

v=transform(start);

调用printres(&g,arrAcc,path,start,end);输出<<换行;

For(i0to56){//使用完弗洛伊德算法之后,g.arrAcc已经变了,所以需要邻接矩阵

For(j0to56)

{

arrAcc[i][j]←matrix[i][j];

}

}

调用Dijkstra(56,v,distence,prev,matrix);

//最短路径长度

输出<<"根据迪杰斯特拉算法得,最少经过的车站数量:

"<

//路径

输出<<"经过的车站路径编号:

";

调用searchPath(prev,v,n);

}

}

函数调用关系图

4调试分析

1)调试过程中遇到的问题及其解决办法,以及对整个设计和实现过程的回顾讨论与分析;

问题一:

在进行完floyd算法之后,邻接矩阵arrAcc[56][56]的值已经变化了,

此时再利用这个矩阵进行接下来的Dijkstra算法,以及在用户九子那个重复输入,重复调用floyd与Dijkstra函数,都是不能得出正确的结果的。

在对这个点进行调试的时候,就颇费了些功夫,最后不得不先将arrAcc数组储存起来,每次在调用floyd之前,都新建一个matrix[56][56]数组,将arrAcc[56][56]数组中的值都复制进来,之后让floyd函数调用matrix数组,而不再调用arrAcc数组。

问题二:

在借用网上的代码的时候,网上的算法中,无论矩阵还是数组,下标都是从1开始的,但我自己所建的数组,为了节省内存,都是从0开始的,所以在将从1开始的数组以及矩阵转化为从0开始的数组与矩阵的时候,废了很

多麻烦。

但无论是从0开始,还是从1开始,只是表达数组与矩阵的思想不同,效果都是一样的。

问题三:

在设计实现用户的重复输入的过程中,一直找不到如果用户输入错误之

后,重新输入时,输入正确之后,结束循环的条件,就一直陷入死循环中。

最后,就设置了judge1和judge2两个数来记录是否能够在已有的站点中找到所输入的站点。

在依据用户输入的站点对已有的站点数组进行遍历的时候,在数组中的站点与输入的站点不相同的时候,用0对judge1/judge2进行重复赋值,一旦找到站点数组中的某个站点与用户所输入的站点相同的时候,用 1对

judge1/judge2进行赋值,并且break。

判断循环的条件就是judge1/judge2==0。

问题四:

在整个程序中涉及到很多对矩阵进行的操作,包括很多对于矩阵中的值的修改,在很多情况下,在代码运行错误,以及出现意想不到的结果的时候,通过输出中间变量矩阵来判断程序那里出现了问题是非常方便的。

所以在整个程序编写的过程中,也写了很多的将程序运行过程中生成的矩阵输出的代码,以便弄清楚代码哪里出现了问题。

2)算法的时空分析。

空间复杂度:

在整个程序运行的过程中,定义的变量有

arrAcc[56][56],s[56],queue[56][56],distance[56][56],prev[56][56],path[56][56],matrix[56][56]以,

及三个,大小总共为64的用于记录站点编号的数组。

所以程序的空间复杂度为O(6608).

时间复杂度:

程序中所有的循环都是以56为单位的。

在整个程序运行的过程中,共进行了以56为单位的单重循环5次,双重循环8次,三重循环1次。

所以整个程序的时间复杂度为O(200984).

5测试结果

当输入的车站名满足要求时:

所设计的程序能够实现重复输入,更大程度的满足用户需求。

当输入的车站名不存在时:

起点终点均不存在:

起点存在,终点不存在:

终点存在,起点不存在:

当用户重复输入错误的时候:

在设计程序的过程中,考虑了用户输入错误的情况。

在用户输入错误的情况下,提示用户是起点,还是终点输入错误,之后提示用户确认后重新输入。

并且支持程序的重复运行,直到用户不再需要查询路径的时候,退出程序。

6实验总结

本次实验主要就是基于弗洛里德与迪杰斯特拉算法进行最短路径的求解,两种算法虽然原理不同,但总的来说,都是对矩阵进行操作,从而得到自己想要的结果。

网上也有很多有关弗洛伊德算法与迪杰斯特拉算法的代码,但将网上的代码改编为自己想要的基于地铁换乘的代码还是有很大难度的。

首先需要考虑的就是在定义储存所有站点的数组的时候,地铁线路的交叉点应处在数组的什么位置,处于所有站点的末尾与处在站点的中间会是两种不同的处理方法。

并且网上的很多代码都是数字作为整个图的节点,但地铁换乘的节点是站点名字,是string类型的量。

在将代码从基于数字到基于string也是破费了写功夫。

创新之处:

1.在初始化邻接矩阵的时候,依据每个站点在总的存储站点的数组里的顺序,定义了分别表示三条地铁线路的int型数组。

用int型数组中的值来表示每个站点的下标,这样在初始化邻接矩阵的时候,只用写三个循环,就还能搞定,大大减少了代码的工作量。

2.刚开始在看书上的关于floyd算法的时候,floyd算法的输出结果只有矩阵,本来以为基于

floyd算法只能得到最短路径的长度,无法得出最短路径的车站编号。

但查阅网上那个的资料,才发现基于floyd算法仍旧是可以输出最短路径的编号的,所以在程序输出的时候,就依据floyd算法输出了最短路径的车站编号。

3.程序增加的查错的功能,在用户输入的站点名不存在的时候,会提示用户输入错误,允许用户确认后重新输入,如果仍旧出错,则可以继续输入,直到输入正确为止。

并且在用户重复输入错误的时候,面对满屏的字母,辨别哪个是正确的起点和终点是很麻烦的,所以增加了一项用来说明最终的起点与终点的功能,方便用户。

缺点:

为了让网上的代码能够在我的电脑上运行,我不惜一切代价改进代码对接的接口,甚至不惜建很多很多的用来进行接口转化的数组,而不太敢于对算法的内部进行必要的调整,以至于浪费了比较多的内存空间。

并且本来设想的是,将按键与代码设置联系,让用户选择不同的操作,当按“开始键”的时候,程序运行,当按“继续”键的时候,用户重复输入起

点与终点,当按“结束键”的时候,程序运行结束。

但因为时间原因,最后实现的仅仅只是将程序重复执行,允许用户重复输入,并未将程序与键盘上的按键建立联系。

感受:

在整个编程的过程中,自己心心念念想的只是如何让程序运行,输出自己想要的结果,

并未考虑太多程序的时间复杂度与空间复杂度的问题,这也就导致了代码不够简洁,可能多谢了很多的循环,并且新建了很多的没有用,或者不必要的数组。

并且也许是因为对于弗洛里德和迪杰斯特拉算法并没有透彻理解,自己也并没有尝试独立地写一写这两个算法的程序,而只是对网上的代码进行筛选,找到能够满足自己需求的代码,进行改编。

也同样是这个原因,自己在改代码的过程中更加侧重的是对于不同函数之间的接口数据的转化预处理,并未触及太多floyd与Dijkstra算法内部的东西,并且对于整个算法结构的改编也不是很多。

这也是一大遗憾。

7附录

只有一个“源.cpp”文件。

所写的程序修改了好几个版本,最终还是摒弃了头文件,源文件的方式,而是仅仅写了一个源文件。

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

当前位置:首页 > IT计算机 > 电脑基础知识

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2