单源最短路径,单目的地最短路径,单结点对最短路径,所有结点对最短路径。最短路径的子路径也是最短路径。最短路径不包含环路。松弛操作,三角不等式。前驱子图,最短路径树。
Bellman-Ford算法。边总数-1次循环,每次循环将所有边进行松弛操作,最终得到最短路径,或者报错存在w为负数的环路。
relax(u, v){
if(u.d+w(u, v)<v.d) {v.d=u.d+w(u,v); v.p=u;}
}
Bellman_Ford(G, w, s){
for i = 1 to |G.V|-1 {
for each edge(u, v) in G.E
relax(u, v);
}
for each edge(u, v) in G.E
if(v.d> u.d+w(u, v) ) throw error
}
B-F适用于边有环路、有负值w的情形。总运行时间为O(EV)。若是不包括环路,则可用基于拓扑排序的简化版。运行时间为O(E+V)。
DAG_shortest_paths(G, w, s){
topologically sort the G.V as P[]
for each vertex u in P[]
for each (u, v) in Adj[u]
relax(u, v);
}
因为每条边只松弛一次,所以节省了运行时间。
Dijkstra算法适用于所有w无负值的情况。类似于Prim最小生成树算法,维持一个子集,然后每次加入一个新元素。加入新元素的选择标准是u.d最小,然后松弛所有边(u, v)中的v。总运行时间为O(|V|^2)。
Dijkstra(G, w, s){
Q=G.V;
while(Q not empty){
u=Q.extract_min;
for each (u, v) in Adj[u]{
relax(u, v);
}
}
}
差分约束与最短路径。Ax<=b 约束条件下,寻找c'x的最大值。A是矩阵,x、b、c是向量。若A每行只有两个元素,分别取值+/-1,则称差分约束系统。约束图。一个可行解是x=(δ(x0, x1), δ(x0, x2).. .δ(x0, xn) ),约束图不能有负值的环路。x0是约束图中虚拟结点,以0权重直接连接所有结点。
所有结点对的最短路径问题。构造一个n*n的矩阵,L[i, j]是i结点到j结点的最短路径。下一次迭代时,L(n)[i, j]=min{L(n-1)[i, k ]+w[k, j ] }。在n-1次迭代后,得到所有结点对之间的最小路径。
extend_shortest_paths(L, w){
for i = 1 to n
for j =1 to n
New_L[i, j]=min{L[i, k] + w[k, j] } for all k= 1 to n
return New_L[i, j]
}
slow_all_pairs_shortest_paths(w){
L[1]=w
for m=2 to n-1
L[m]=extend_shortest_paths(L[m-1], w)
}
Floyd-Warshall算法。寻找结点i到结点j的最短路径,可以考虑有某结点k,先从1到k-1的点集里找i到k的最短路径P1,再从1到k-1的点集里找到k到j的最短路径P2,则有两种可能性。从1到k-1的点集中寻找i到j的最短路径,要么不包含k,要么包含k且等于P1+P2. 那么从k=1到n迭代,计算可以得到所有点对间的最短路径。运行时间O(n^3)。
Floyd_Warshall(w){
L[0][i, j]=w;
for k = 1 to n
for i = 1 to n
for j = 1 to n
L[k][i, j]=min(L[k-1][i, j], L[k-1][i, k] + L[k-1][k, j] )
}