Definition
对于无权图来说,我们可以用广度优先搜索方法(BFS, Breadth-First-Search)查找最短路径,但是当图是带权图时,带权路径长度为:从一个顶点到其余任意一个顶点的一条路径所经过的边上的权值之和,带权路径最短的即为最短路径
Dijkstra 算法
Definition
求某个源点到其他各顶点的最短路径,
S
S
S 集合记录已经求得最短路径的顶点,通过一个数组 s[]
来实现,初始化值为
0
0
0,放入集合置为
1
1
1,此外还需要两个数组:
dist[]
:记录当前从源点 v 0 v_{0} v0 到其他各顶点的路径长度,假如边的权重用一个二维数组来存储的话,例如arcs[][]
,如果两点之间没有路径则为 ∞ \infin ∞,这样我们初始化dist[i]=arcs[0][i]
,path[]
:表示从源点到某个顶点最短路径的前驱顶点(即终点的前一个顶点,然后我们再找到这个顶点的前一个顶点,这样回溯可以得到从源点到达每一个顶点的最短路径)
算法步骤其实很简单,有点类似Prim算法,就是每次从剩下的顶点集中选择dist[i]
最小的顶点添加到
S
S
S 中,然后对加入新顶点之后的dist[]
进行修改,重复这个过程直到所有的顶点都被加入到
S
S
S 中,这样我们就得到了从源点到每个顶点之间的最短路径了
Performance
这个算法同样是采取贪心算法的思想,采用邻接矩阵和带权的邻接表法时间复杂度都是 O ( ∣ V ∣ 2 ) O(|V|^{2}) O(∣V∣2)
Problem
算法的局限性:
- 如果图包含负边,算法不适用
- 到特定顶点的最短距离求解方法复杂
Floyd算法
如果你熟悉 W a r s h a l l Warshall Warshall 算法的话,那相信这个算法对你来说是简单易懂的,因为实际上都是考虑中间点—— k k k,即不断尝试往一个顶点和另外一个顶点之间添加一个新的中间点,观察是否会得到更优的路径长度,然后不断更新这个路径长度,直到我们考虑了所有的中间点,最终我们就得到了我们的最短路径和最短路径长度了,复杂度为 O ( ∣ V ∣ 3 ) O(|V|^{3}) O(∣V∣3)。
这个算法允许负边存在,但不允许负环;此外这个算法同样适用于带权无向图(可以把无向边看成往返边)
Implementation
void floydMinPath(Graph G){
int n = G.vexNum;
// suppose that the very graph is stored by adjacency matrix with element A[i][j]
// stores length of path from vi to vj
int** A = G.E;
// after k pass could we obtain
for(int k = 0; k < vexNum; k++){
for(int i = 0; i < vexNum; i++){
for(int j = 0; j < vexNum; j++){
int temp = A[i][k] + A[k][j];
A[i][j] = A[i][j] > temp?A[i][j]:temp;
}
}
}
}