单源最短路径问题 (Single-Source Shortest-PathsProblem)
单源最短路径问题:已知有向带权图(简称有向网)G=(V,E),找出从某个源点s∈V到V中其余各顶点的最短路径。
1、边上权值相等的有向网的单源最短路径 用求指定源点的BFS生成树的算法可解决
2、迪杰斯特拉(Dijkstra)算法求单源最短路径 由Dijkstra提出的一种按路径长度递增序产生各顶点最短路径的算法。
(1)按路径长度递增序产生各顶点最短路径 若按长度递增的次序生成从源点s到其它顶点的最短路径,则当前正在生成的最短路径上除终点以外,其余顶点的最短路径均已生成(将源点的最短路径看作是已生成的源点到其自身的长度为0的路径)。
(2)算法基本思想 设S为最短距离已确定的顶点集(看作红点集),V-S是最短距离尚未确定的顶点集(看作蓝点集)。
①初始化 初始化时,只有源点s的最短距离是已知的(SD(s)=0),故红点集S={s},蓝点集为空。
②重复以下工作,按路径长度递增次序产生各顶点最短路径 在当前蓝点集中选择一个最短距离最小的蓝点来扩充红点集,以保证算法按路径长度递增的次序产生各顶点的最短路径。 当蓝点集中仅剩下最短距离为∞的蓝点,或者所有蓝点已扩充到红点集时,s到所有顶点的最短路径就求出来了。
注意: ①若从源点到蓝点的路径不存在,则可假设该蓝点的最短路径是一条长度为无穷大的虚拟路径。 ②从源点s到终点v的最短路径简称为v的最短路径;s到v的最短路径长度简称为v的最短距离,并记为SD(v)。
(3)在蓝点集中选择一个最短距离最小的蓝点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>,且从s到v的路径上没有中间点,因为该路径仅含一条边<s,v>。
注意: 在蓝点集中选择一个最短距离最小的蓝点k来扩充红点集是Dijkstra算法的关键
(4)k扩充红点集s后,蓝点集估计距离的修改 将k扩充到红点后,剩余蓝点集的估计距离可能由于增加了新红点k而减小,此时必须调整相应蓝点的估计距离。 对于任意的蓝点j,若k由蓝变红后使D[j]变小,则必定是由于存在一条从s到j且包含新红点k的更短路径:P=<s,…,k,j>。且D [j]减小的新路径P只可能是由于路径<s,…,k>和边<k,j>组成。 所以,当length(P)=D[k]+w<k,j>小于D[j]时,应该用P的长度来修改D[j]的值。
(5)Dijkstra算法 Dijkstra(G,D,s){ //用Dijkstra算法求有向网G的源点s到各顶点的最短路径长度 //以下是初始化操作 S={s};D[s]=0; //设置初始的红点集及最短距离 for( all i∈ V-S )do //对蓝点集中每个顶点i D[i]=G[s][i]; //设置i初始的估计距离为w<s,i> //以下是扩充红点集 for(i=0;i<n-1;i++)do{//最多扩充n-1个蓝点到红点集 D[k]=min{D[i]:all i V-S}; //在当前蓝点集中选估计距离最小的顶点k if(D[k]等于∞) return; //蓝点集中所有蓝点的估计距离均为∞时, //表示这些顶点的最短路径不存在。 S=S∪{k}; //将蓝点k涂红后扩充到红点集 for( all j∈V-S )do //调整剩余蓝点的估计距离 if(D[j]>D[k]+G[k][j]) //新红点k使原D[j]值变小时,用新路径的长度修改D[j], //使j离s更近。 D[j]=D[k]+G[k][j]; } } 【例】对有向网G
8以0为源点执行上述算法的过程及红点集、k和D向量的变化见【
参见动画演示】
简单也可以用以下方式理解
简单也可以用以下方式理解
解决最短路径问题最著名的算法是Djikstra算法。这个算法的实现基于图的邻接矩阵表示法,它不仅能够找到任意两点的最短路径,还可以找到某个指定点到其他所有顶点的最短路径。 此算法的基本思想是: 1> 选中指定的顶点,列出此顶点到其他的顶点的权值,不相邻的为无穷大 2> 从以上权值中选出最小值,此最小值就是起始点到对应顶点的最短路径,并标记这个对应顶点 3> 将起始点到其他未标记的顶点的直接距离与起始点到刚才标记顶点加上标记顶点到其他顶点距离的和比较,如果 后者小,则更新对应的权值。 4> 转2 此算法的java实现如下:
class
DistPar
{ public int distance; public int parentVert; public DistPar(int pv,int d){ distance=d; parentVert=pv; } }
class
Vertex
{ public char label; public boolean isInTree; public Vertex(char lab){ label=lab; isInTree=false; } }
public
class
Path
{ private final int MAX_VERTS=20; private final int INFINITY=1000000; private Vertex vertexList[]; private int adjMat[][]; private int nVerts; private int nTree; private DistPar sPath[]; private int currentVert; private int startToCurrent; /** Creates a new instance of Path */ public Path() { vertexList=new Vertex[MAX_VERTS]; adjMat=new int[MAX_VERTS][MAX_VERTS]; nVerts=0; nTree=0; for (int j = 0; j < MAX_VERTS; j++) { for (int k = 0; k < MAX_VERTS; k++) { adjMat[j][k]=INFINITY; } } sPath=new DistPar[MAX_VERTS]; } public void addVertex(char lab){ vertexList[nVerts++]=new Vertex(lab); } public void addEdge(int start,int end,int weight){ adjMat[start][end]=weight; } //找到选定顶点到其他顶点的最短路径(此处选定0号顶点) public void path(){ int startTree=0; vertexList[startTree].isInTree=true; nTree=1; for(int j=0;j<nVerts;j++){ int tempDist=adjMat[startTree][j]; sPath[j]=new DistPar(startTree,tempDist); } while(nTree<nVerts){ int indexMin=getMin(); int minDist=sPath[indexMin].distance; if(minDist==INFINITY){ System.out.println("There are unreachable vertices"); break; }else{ currentVert=indexMin; startToCurrent=sPath[indexMin].distance; } vertexList[currentVert].isInTree=true; nTree++; adjust_sPath(); } displayPaths(); nTree=0; for (int j = 0; j < nVerts; j++) { vertexList[j].isInTree=false; } } //得到当前最短路径数组中的最小值 public int getMin(){ int minDist=INFINITY; int indexMin=0; for (int j = 1; j < nVerts; j++) { if(!vertexList[j].isInTree&&sPath[j].distance<minDist){ minDist=sPath[j].distance; indexMin=j; } } return indexMin; } //更新最短路径数组 public void adjust_sPath(){ int column=1; while(column<nVerts){ if(vertexList[column].isInTree){ column++; continue; } int currentToFringe=adjMat[currentVert][column]; int startToFringe=startToCurrent+currentToFringe; int sPathDist=sPath[column].distance; if(startToFringe<sPathDist){ sPath[column].parentVert=currentVert; sPath[column].distance=startToFringe; } column++; } } public void displayPaths(){ } }