算法分析与设计 求带权最短路径 课程设计.docx
《算法分析与设计 求带权最短路径 课程设计.docx》由会员分享,可在线阅读,更多相关《算法分析与设计 求带权最短路径 课程设计.docx(8页珍藏版)》请在冰豆网上搜索。
![算法分析与设计 求带权最短路径 课程设计.docx](https://file1.bdocx.com/fileroot1/2022-12/7/a0f66f7c-45eb-43ce-b609-3b4182126e3e/a0f66f7c-45eb-43ce-b609-3b4182126e3e1.gif)
算法分析与设计求带权最短路径课程设计
《算法设计与分析》
课程设计报告
题目:
求带权最短路径
专业:
****
学号:
****
姓名:
****
指导教师:
***
成绩:
**年*月**日
一、问题描述
给定n个顶点的带权有向图G=(V,E),W=(
)为G的带权邻接矩阵。
定义如下:
=
对于每一对顶点u,v
V,试用动态规划方法,求从u到v的带权最短路径长度,其中路径权值为这条路径所有边上的权值之和。
二、设计说明
1、基本要求:
利用Dijkstra算法,寻找有向图中最短路径。
2、算法说明:
Dijkstra算法是由荷兰计算机科学家艾兹格·迪科斯彻发现的。
算法解决的是有向图中最短路径问题。
Dijkstra算法的输入包含了一个有权重的有向图G,以及G中的一个来源顶点S。
我们以V表示G中所有顶点的集合。
图中的每一个边,都是两个顶点所形成的有序元素对。
(u,v)表示从顶点u到v有路径相连。
假设E为所有边的集合,而边的权重则由权重函数w:
E→[0,∞]定义。
因此,w(u,v)就是从顶点u到顶点v的非负花费值(cost)。
边的花费可以想像成两个顶点之间的距离。
任两点间路径的花费值,就是该路径上所有边的花费值总和。
已知有V中有顶点s及t,Dijkstra算法可以找到s到t的最低花费路径(i.e.最短路径)。
这个算法也可以在一个图中,找到从一个顶点s到任何其他顶点的最短路径。
这个算法是通过为每个顶点v保留目前为止所找到的从s到v的最短路径来工作的。
初始时,源点s的路径长度值被赋为0(d[s]=0),同时把所有其他顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于V中所有顶点v除s外d[v]=∞)。
当算法结束时,d[v]中储存的便是从s到v的最短路径,或者是无穷大(如果路径不存在的话)。
Dijstra算法的基础操作是边的拓展:
如果存在一条从u到v的边,那么从s到v的最短路径可以通过将边(u,v)添加到s到u的尾部来拓展。
这条路径的长度是d[u]+w(u,v)。
如果这个值比目前已知的d[v]的值要小,我们可以用新值来替代当前d[v]中的值。
拓展边的操作一直执行到所有的d[v]都代表从s到v最短路径的花费。
这个算法经过适当的组织因而当d[u]达到它最终的值的时候,每条边(u,v)都只被拓展一次。
算法维护两个顶点集S和Q。
集合S保留了我们已知的所有d[v]的值已经是最短路径的值顶点,而集合Q则保留其他所有顶点。
集合S初始状态为空,而后每一步都有一个顶点从Q移动到S。
这个被选择的顶点是Q中拥有最小的d[u]值的顶点。
当一个顶点u从Q中转移到了S中,算法对每条外接边(u,v)进行拓展。
3、算法思想:
(1)设S为最短距离已确定的顶点集(看作红点集),V-S是最短距离尚未确定的顶点集(看作蓝点集)。
①初始化:
初始化时,只有源点s的最短距离是已知的(SD(s)=0),故红点集S={s},蓝点集为空。
②重复以下工作,按路径长度递增次序产生各顶点最短路径。
在当前蓝点集中选择一个最短距离最小的蓝点来扩充红点集,以保证按路径权重递增的次序来产生各顶点的最短路径。
当蓝点集中仅剩下最短距离为∞的蓝点,或者所有蓝点已扩充到红点集时,s到所有顶点的最短路径就求出来了。
注意:
①若从源点到蓝点的路径不存在,则可假设该蓝点的最短路径是一条长度为无穷大的虚拟路径。
②从源点s到终点v的最短路径简称为v的最短路径;s到v的最短路径长度简称为v的最短距离,并记为SD(v)。
(2)在蓝点集中选择一个最短距离最小的蓝点k来扩充红点集
根据按长度递增序产生最短路径的思想,当前最短距离最小的蓝点k的最短路径是:
源点,红点1,红点2,…,红点n,蓝点k。
距离为:
源点到红点n最短距离+<红点n,蓝点k>边长
为求解方便,设置一个向量D[0..n-1],对于每个蓝点v∈V-S,用D[v]记录从源点s到达v且除v外中间不经过任何蓝点(若有中间点,则必为红点)的"最短"路径长度(简称估计距离)。
若k是蓝点集中估计距离最小的顶点,则k的估计距离就是最短距离,即若D[k]=min{D[i]i∈V-S},则D[k]=SD(k)。
初始时,每个蓝点v的D[c]值应为权w,且从s到v的路径上没有中间点,因为该路径仅含一条边。
注意:
在蓝点集中选择一个最短距离最小的蓝点k来扩充红点集是Dijkstra算法的关键。
(3)k扩充红点集s后,蓝点集估计距离的修改
将k扩充到红点后,剩余蓝点集的估计距离可能由于增加了新红点k而减小,此时必须调整相应蓝点的估计距离。
对于任意的蓝点j,若k由蓝变红后使D[j]变小,则必定是由于存在一条从s到j且包含新红点k的更短路径:
P=。
且D[j]减小的新路径P只可能是由于路径和边组成。
所以,当length(P)=D[k]+w小于D[j]时,应该用P的长度来修改D[j]的值。
例:
如下在有向网G8中,假定以顶点0为源点,则它则其余各顶点的最短路径按路径递增序排列如下表所示:
三、源程序代码
packagesinboy.datastructure;
importjava.util.ArrayList;
publicclassDijkstra...{
staticArrayListmap=null;
staticArrayListredAgg=null;
staticArrayListblueAgg=null;
staticSide[]parents=null;
publicstaticvoidmain(String[]args)...{
//初始化顶点集
int[]nodes=...{0,1,3,2,4,5,6};
//初始化有向权重图
map=newArrayList();
map.add(newSide(0,1,10));
map.add(newSide(0,3,30));
map.add(newSide(0,4,100));
map.add(newSide(1,2,50));
map.add(newSide(2,4,10));
map.add(newSide(3,2,20));
map.add(newSide(3,4,60));
map.add(newSide(4,5,50));
map.add(newSide(3,5,60));
map.add(newSide(5,6,10));
map.add(newSide(3,6,80));
//初始化已知最短路径的顶点集,即红点集,只加入顶点0
redAgg=newArrayList();
redAgg.add(nodes[0]);
//初始化未知最短路径的顶点集,即蓝点集
blueAgg=newArrayList();
for(inti=1;iblueAgg.add(nodes[i]);
//初始化每个顶点在最短路径中的父结点,及它们之间的权重,权重-1表示无连通
parents=newSide[nodes.length];
parents[0]=newSide(-1,nodes[0],0);
for(inti=0;iintn=blueAgg.get(i);
parents[i+1]=newSide(nodes[0],n,getWeight(nodes[0],n));
}
//找从蓝点集中找出权重最小的那个顶点,并把它加入到红点集中
while(blueAgg.size()>0)...{
MinShortPathmsp=getMinSideNode();
if(msp.getWeight()==-1)
msp.outputPath(nodes[0]);
else
msp.outputPath();
intnode=msp.getLastNode();
redAgg.add(node);
//如果因为加入了新的顶点,而导致蓝点集中的顶点的最短路径减小,则要重要设置
setWeight(node);
}}
得到一个节点的父节点
@paramparents
@paramnode
@return
publicstaticintgetParent(Side[]parents,intnode)...{
if(parents!
=null)...{
for(Sidend:
parents)...{
if(nd.getNode()==node)...{
returnnd.getPreNode();
}}}
return-1;
}
重新设置蓝点集中剩余节点的最短路径长度
@parampreNode
@parammap
@paramblueAgg
publicstaticvoidsetWeight(intpreNode)...{
if(map!
=null&&parents!
=null&&blueAgg!
=null)...{
for(intnode:
blueAgg)...{
MinShortPathmsp=getMinPath(node);
intw1=msp.getWeight();
if(w1==-1)
continue;
for(Siden:
parents)...{
if(n.getNode()==node)...{
if(n.getWeight()==-1||n.getWeight()>w1)...{
n.setWeight(w1);
n.setPreNode(preNode);//重新设置顶点的父顶点
break;
}}}}}}
得到两点节点之间的权重
@parammap
@parampreNode
@paramnode
@return
publicstaticintgetWeight(intpreNode,intnode)...{
if(map!
=null)...{
for(Sides:
map)...{
if(s.getPreNode()==preNode&&s.getNode()==node)
returns.getWeight();
}}
return-1;
}
从蓝点集合中找出路径最小的那个节点
@parammap
@paramblueAgg
@return
publicstaticMinShortPathgetMinSideNode()...{
MinShortPathminMsp=null;
if(blueAgg.size()>0)...{
intindex=0;
for(intj=0;jMinShortPathmsp=getMinPath(blueAgg.get(j));
if(minMsp==null||msp.getWeight()!
=-1&&msp.getWeight()minMsp=msp;
index=j;
}}
blueAgg.remove(index);
}
returnminMsp;
}
得到某一节点的最短路径(实际上可能有多条,现在只考虑一条)
@paramnode
@return
publicstaticMinShortPathgetMinPath(intnode)...{
MinShortPathmsp=newMinShortPath(node);
if(parents!
=null&&redAgg!
=null)...{
for(inti=0;iMinShortPathtempMsp=newMinShortPath(node);
intparent=redAgg.get(i);
intcurNode=node;
while(parent>-1)...{
intweight=getWeight(parent,curNode);
if(weight>-1)...{
tempMsp.addNode(parent);
tempMsp.addWeight(weight);
curNode=parent;
parent=getParent(parents,parent);
}else
break;
}
if(msp.getWeight()==-1||tempMsp.getWeight()!
=-1&&msp.getWeight()>tempMsp.getWeight())
msp=tempMsp;
}}
returnmsp;
}}
图中的有向边,包括节点名及他的一个前向节点名,和它们之间的权重
classSide...{
privateintpreNode;//前向节点
privateintnode;//后向节点
privateintweight;//权重
publicSide(intpreNode,intnode,intweight)...{
this.preNode=preNode;
this.node=node;
this.weight=weight;
}
publicintgetPreNode()...{
returnpreNode;
}
publicvoidsetPreNode(intpreNode)...{
this.preNode=preNode;
}
publicintgetNode()...{
returnnode;
}
publicvoidsetNode(intnode)...{
this.node=node;
}
publicintgetWeight()...{
returnweight;
}
publicvoidsetWeight(intweight)...{
this.weight=weight;
}}
classMinShortPath...{
privateArrayListnodeList;//最短路径集
privateintweight;//最短路径
publicMinShortPath(intnode)...{
nodeList=newArrayList();
nodeList.add(node);
weight=-1;
}
publicArrayListgetNodeList()...{
returnnodeList;
}
publicvoidsetNodeList(ArrayListnodeList)...{
this.nodeList=nodeList;
}
publicvoidaddNode(intnode)...{
if(nodeList==null)
nodeList=newArrayList();
nodeList.add(0,node);
}
publicintgetLastNode()...{
intsize=nodeList.size();
returnnodeList.get(size-1);
}
publicintgetWeight()...{
returnweight;
}
publicvoidsetWeight(intweight)...{
this.weight=weight;
}
publicvoidoutputPath()...{
outputPath(-1);
}
publicvoidoutputPath(intsrcNode)...{
Stringresult="[";
if(srcNode!
=-1)
nodeList.add(srcNode);
for(inti=0;iresult+=""+nodeList.get(i);
if(iresult+=",";
}
result+="]:
"+weight;
System.out.println(result);
}
publicvoidaddWeight(intw)...{
if(weight==-1)
weight=w;
else
weight+=w;
}}
运行结果如下:
[0,1]:
10
[0,3]:
30
[0,3,2]:
50
[0,3,2,4]:
60
[0,3,5]:
90
[0,3,5,6]:
100
四、其他最短路径问题
最短路径问题的提法很多,其它的最短路径问题均可用单源最短路径算法予以解决:
①单目标最短路径问题(Single-DestinationShortest-PathsProblem):
找出图中每一顶点v到某指定顶点u的最短路径。
只需将图中每条边反向,就可将这一问题变为单源最短路径问题,单目标u变为单源点u。
②单顶点对间最短路径问题(Single-PairShortest-PathProblem):
对于某对顶点u和v,找出从u到v的一条最短路径。
显然,若解决了以u为源点的单源最短路径问题,则上述问题亦迎刃而解。
而且从数量级来说,两问题的时间复杂度相同。
③所有顶点对间最短路径问题(All-PairsShortest-PathsProblem):
对图中每对顶点u和v,找出u到v的最短路径问题。
这一问题可用每个顶点作为源点调用一次单源最短路径问题算法予以解决。
五、总结
最短路径算法关键是先把已知最短路径顶点集(只有一个源点)和未知的顶点分开,然后依次把未知集合的顶点按照最短路径(这里特别强调一下是源点到该顶点的路径权重和,不仅仅是指它和父结点之间的权重,一开始就是没有在这个问题上弄清楚)加入到已知结点集中。
在加入时可以记录每个顶点的最短路径,也可以在加入完毕后回溯找到每个顶点的最短路径和权重。
参考文献:
[1]《数据结构》严蔚敏,吴伟民清华大学出版社出版
[2]《算法设计与分析》霍红卫清华大学出版社出版
[3]《算法设计与分析》郑宗汉,郑晓明清华大学出版社