最短路径——Dijkstar算法
Dijkstar算法思想:
较抽象:
设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
较具体:
Dijkstar算法的核心思想就是通过一次一次的迭代,逐个寻找起始顶点到图中每一个顶点的最短路径,并在确定一个顶点的最短路径之后,设置标志位,同时根据最新确定的顶点的最短路径,对其他与之相关顶点的最短路径进行修正和更新。
一个算法执行过程的动图:
算法实例:
如上图所示,是一个带有权值的连通网,根据上图可以列写出该连通网的邻接矩阵:
算法流程:
D数组表示原点到各点的最短路径,P数组表示各点最短路径上的前驱节点。
算法实现过程中主要有两个循环:
第一个循环:在没有确定最短路径的顶点集合中,计算每一个顶点目前的最短路径,选择目前最短路径最短的顶点,则这个目前的最短路径就是这个顶点真正的最短路径
第二个循环:根据已经确定最短路径的顶点,利用确定最短路径的顶点作为前驱顶点,更新其余所有顶点的当前最短路径。
如此不断地依次执行这两个循环,构成一个大循环,一直到所有顶点都找到最短路径为止。
第一次大循环:
第一个for循环,首先在未确定最短路径的所有顶点中寻找与V0直接相连,且权值最小的顶点,得到k = 1(k保存前驱顶点);min = 1(min保存到达前驱顶点的最短路径权值和),在这个for循环中,找到的这个顶点可以直接确定找到了最短路径(因为每次在第二个for循环中,都进行了对于其他未确定最短路径的顶点的路径优化)。
第二个for循环,对于路径进行优化,优化原则为:如果 [到达前驱顶点的权值和(即min)+ 前驱顶点到达其他未确定最短路径的顶点的权值 < 当前D数组中达到该顶点的权值和 ]则说明当前到达该顶点的路径不够优化,此时即可采用经过当前前驱顶点到达那个顶点的路径替换之前的路径,即同时更新P数组,将当前的k中保存的前驱顶点作为那个顶点的前驱。
循环结果:
第二次大循环:
相同的原理进行一次迭代(注意自己过流程的时候,对照上面的邻接矩阵)。
循环结果:
第三次大循环:
相同的原理进行一次迭代
循环结果:
如此循环n次(n为顶点数),即可已得到最短路径
结果如下:
根据D数组可以得到从起始点到达任何一个顶点的最短路径权值和,根据P数组可以得到每一个顶点的最短路径中的前驱顶点,进而可以连接生成最短路径,该例题中到达V8的最短路径为:
算法实现代码:
#define MAXVEX 9
#define INFINITY 65535
/* 用来存储最短路径下标的数组 */
typedef int Patharc[MAXVEX];
/* 用来存储到各点最短路径的权值和 */
typedef int ShortPathTable[MAXVEX];
void ShortestPath_Dijkstar(MGragh G,int V0,Patharc *P,ShortPathTable *D){
int v,w,k,min;
/* final[w] = 1表示已经求得顶点Vo到Vw的最短路径 */
int final[MAXVEX];
/* init */
for(v=0;v<G.numVertexes;v++){
/* 全部顶点初始化为未找到最短路径 */
final[v] = 0;
/* 起始点V0到其他点之间的最短路径数组赋初始值:即邻接矩阵中V0行的值 */
/* 即到与V0有有直接连线的点的最短路径为边的权值;无直接连线的点的最短路径为最大值 */
(*D)[v] = G.arc[V0][v];
/* 初始化路径数组P为0 */
(*P)[v] = 0;
}
/* V0到V0的路径为0 */
(*D)[V0] = 0;
/* V0到V0不需要求路径 */
final[V0] = 1;
/* 主循环 每次求得V0到某个V顶点的最短路径 */
/* 从v=1开始循环因为v0点的最短路径已经自动赋值为0 */
for(v=1;v<G.numVertexes;v++){
min = INFINITY;
/* 第一个for循环 */
for(w=0;w<G.numVertexes;w++){
/* 判断final数组(即未求得最短路径) */
/* 仅对未确定最短路径的顶点 在D数组中寻找最小权值 */
/* 因为经过下一个for循环的最短路径修正之后,未确定最短路径的顶点中到V0权值最小的顶点经过的必然是最短路径 */
if(!final[w] && (*D)[w]<min){
/* k暂时保存前驱顶点 */
k = w;
/* min暂时保存到达当前的最短路径 */
min = (*D)[w];
}
}
/* 标志当前顶点以找到了最短路径 */
final[k] = 1;
/* 在找到一个新的顶点的最短路径后,修正其他未确定最短路径的顶点的当前最短路径 */
/* 原先可能某些未确定最短路径的顶点,选择的路径不够优化(以此顶点的最优路径对其进行优化) */
/* 第二个for循环 */
for(w=0;w<G.numVertexes;w++){
/* 前驱顶点已经确认为最短路径(即min) */
/* 如果经过之前保存的前驱顶点的路径比当前D数组中保存的路径短的话 则更新当前最短路径 */
if(!final[w] && (min+G.arc[k][w]<(*D)[w])){
/* 修改当前路径长度 */
(*D)[w] = min+G.arc[k][w];
/* 存放前驱顶点 */
/* P数组存放前驱顶点 完成后即可通过P数组得到最短路径 */
(*P)[w] = k;
}
}
}
}