把从一个顶点v0到到途中其余任意一点v1路径上所有边权值之和定义为该路径带权路径长度,带权路径长度最短的那条路径称作最短路径。
求解最短路径的算法通常依赖于一种性质,两点之间的最短路径也包含了路径上其它顶点的最短路径。
带权有向图最短路径问题分为两类:
1、单源最短路径
2、每一对顶点间的最短路径
Dijkstra算法求单源最短路径问题
求带权有向图某个源点到其余各顶点的最短路径,常用Dijkstra算法。
先以一个例子理清算法执行过程,再看概念
dist[i]表示源点到顶点i的当前最短路径长度,初始值为0
arcs[i][j]表示有向边<i,j>的权值,不存在记为∞
1、首先将源点顶点1加入集合S
2、计算出源点到S外其他顶点的路径长度,
顶点1-顶点2:dist[2]=10
顶点1-顶点5:dist[5]=5
顶点1-顶点3:dist[3]=∞
顶点1-顶点4:dist[4]=∞ (到此算是初始化)
源点-顶点5当前路径长度最短,故将顶点5加入S
3、修改从源点1到S外顶点2、3、4可达的最短路径长度,即
因为dist[5]+arcs[5][2]<dist[2],则dist[2]=dist[5]+arcs[5][2]=8
因为dist[5]+arcs[5][3]<dist[3],则dist[3]=dist[5]+arcs[5][3]=14
因为dist[5]+arcs[5][4]<dist[4],则dist[4]=dist[5]+arcs[5][4]=7
源点-顶点4当前路径长度最短,故将顶点4加入S
4、修改从源点1到S外顶点2、3可达的最短路径长度,即
因为dist[5]+arcs[5][2]<dist[2],则dist[2]=dist[5]+arcs[5][2]=8
因为dist[4]+arcs[4][2]>=dist[2],则dist[2]保持
因为dist[5]+arcs[5][3]>=dist[3],则dist[3]保持
因为dist[4]+arcs[4][3]<dist[3],则dist[3]=dist[4]+arcs[4][3]=13
源点-顶点2当前路径长度最短,故将顶点2加入S
4、修改从源点1到S外顶点3可达的最短路径长度,即
因为dist[5]+arcs[5][3]>=dist[3],则dist[3]保持
因为dist[4]+arcs[4][3]>=dist[3],则dist[3]保持
因为dist[2]+arcs[2][3]<dist[3],则dist[3]=dist[2]+arcs[2][3]=9
总结Dijkstra算法步骤
dist[i]表示源点到顶点i的当前最短路径长度,初始值为0
arcs[i][j]表示有向边<i,j>的权值,不存在记为∞
用集合S存储以求得最短路径的顶点,s[vi]=1表示将顶点i放入S中
path[i]表示从源点到所求顶点i最短路径,顶点i的前驱结点。算法结束之后,可根据此值追溯到从源点到所求结点的最短路径经过的顶点(这里不做说明)
实现思路:
1、初始化,集合S初始值为0,dist[]初始值dist[j]=arcs[0][j],如上例中的初始化
2、循环执行以下两步直到所有的点都包含:
1)从集合外的顶点中找出一个顶点vi,vi满足dist[i]最小,并将顶点i加入集合S中
2)修改从源点到S外所有顶点k的dist[vk]的值,修改规则如下:如若dist[i]+arcs[i][k]<dist[k],则dist[k]=dist[i]+arcs[i][k],dist[i]是已加入集合的顶点的最短路径长度,dist[k]是S外所有顶点中某一个顶点的当前最短路径长度
边上带有负权值,此算法不适用
Floyd算法求各顶点之间最短路径问题
实现思路:实例加概念
拥有n个顶点的图,产生1个n阶方阵序列A0以及不停的迭代产生的A1、A2…An,
用Ak[i][j]表示从顶点vi到顶点vj的路径长度,从vi到vj之间经过的顶点序号数没有大于k的,其中的k表示加入顶点k作为中间顶点,加入规则如下:如果得到的路径比原来的路径长度减少,则新路径代替原来的路径
A0、A1…A(n-1)保存了任意一对顶点当前的最短路径长度,An保存了任意一对顶点最终的最短路径长度
上图的邻接矩阵也就是 A0
A0的基础上vi-vj中间经过顶点1的路径长度即A1
A1的基础上vi-vj中间经过顶点2的路径长度即A2
A2的基础上vi-vj中间经过顶点3的路径长度即A3
此算法允许带负权值的边,但不允许包含有负权值的边组成的回路