大数据结构第二次试验地铁换乘Word文档下载推荐.docx
《大数据结构第二次试验地铁换乘Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《大数据结构第二次试验地铁换乘Word文档下载推荐.docx(15页珍藏版)》请在冰豆网上搜索。
所输入的的站点名不能是所要求的站点名之外的名称。
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);
//将起点与终点转化为存储站点的数组中对应的下标
输出<
<
”起点->
终点”<
endl;
"
根据弗洛伊德算法得,最少经过的车站数量:
"
dis[st][en]+1<
经过的车站路径编号:
;
For(j0to站点数)
{
If(i==起点数码st并且j==终点数码en)//输出路径
stack<
int>
pathrout;
//压栈
k←j;
do{
k←path[i][k];
k入pathrout的栈
}当(k不等于i);
输出<
存储站点的数组s[pathrout的栈顶元素];
弹出pathrout的栈顶元素;
length←栈pathrout的大小;
For(t0tolength)
{
输出<
->
弹出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集合中
//更新distence
If(s[j]为0并且点j到点u的距离c[u][j]小于1000)
newdist←distence[u]+c[u][j];
If(newdist<
distence[j])
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)
输出<
s[que[i]]<
换行;
change_train函数()
distence[56];
记录起点到当前点的最短路径
prev[56];
记录当前点的前一个结点
path[56][56];
记录路径
matrix[56][56];
用于备份的矩阵
arrAcc[56][56];
邻接矩阵
g.verCount=56;
//定义总的站点的数目+1
For(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];
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;
If(s[i]==end){judge2←1;
否则judge2←0;
If(judge2==0){
终点输入错误,请确认后重新输入!
请输入终点:
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);
//最短路径长度
根据迪杰斯特拉算法得,最少经过的车站数量:
distence[n]+1<
//路径
调用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”文件。
所写的程序修改了好几个版本,最终还是摒弃了头文件,源文件的方式,而是仅仅写了一个源文件。