图最短路径之BellmanFord
算法参考地址:
算法的简介
在图中给定一个图形和一个源顶点 src,查找从 src 到给定图中所有顶点的最短路径。该图可能包含负权重边。 我们已经讨论了[Dijkstra针对这个问题的算法]。Dijkstra的算法是一种贪婪算法,时间复杂度为O(V^2),(使用斐波那契堆)的时间复杂度为O((V+E)LogV)。Dijkstra不适用于负权重的图形,Bellman-Ford适用于此类图形。Bellman-Ford也比Dijkstra更简单,并且非常适合分布式系统。但贝尔曼-福特的时间复杂度是O(VE)。
1)负权重在图形的各种应用中都可以找到,比如 (1):计算化学反应经过N环节得到另一种物质,其中(1~N)中的某个环节可能是吸收能量也可能是释放能量。(2):计算电子在原子核外轨道的跃迁吸收或释放的能量等等。 2)Bellman-Ford在分布式系统中工作得更好(比Dijkstra更好)。与Dijkstra不同,我们需要找到所有顶点的最小值,在Bellman-Ford中,边是逐个考虑的。 3)贝尔曼-福特不适用于具有负边的无向图,因为它将被声明为负循环。
算法的过程
以下是详细步骤。 输入:图形和源顶点 src 输出:从 src 到所有顶点的最短距离。如果存在负重周期,则不计算最短距离,报告负重周期。 1) 此步骤将源到所有顶点的距离初始化为无穷大,到源本身的距离初始化为 0。创建大小为 | 的数组 dist[]五|所有值均为无穷大,但 dist[src] 除外,其中 src 是源顶点。 2) 此步骤计算最短距离。执行以下|V|-1倍,其中|五|是给定图形中的顶点数。 .....a) 对每个边缘 u-v 执行跟踪操作。如果 dist[v] > dist[u] + 边缘 uv 的权重,则更新 dist[v] ......................dist[v] = dist[u] + 边缘 uv3 的权重) 此步骤报告图中是否存在负权重周期。对每个边缘u-v 做以下操作......如果 dist[v] > dist[u] + 边缘 uv 的权重,则"图形包含负权重循环" 步骤 3 的想法是,如果图形不包含负权重循环,则步骤 2 保证最短距离。如果我们再次迭代所有边,并为任何顶点获得更短的路径,那么就会出现负权重循环 这是如何工作的?与其他动态规划问题一样,该算法以自下而上的方式计算最短路径。它首先计算路径中最多有一条边的最短距离。然后,它计算最多包含 2 条边的最短路径,依此类推。在外循环的第 i 次迭代之后,将计算最多 i 条边的最短路径。最大|五|– 任何简单路径中的 1 条边,这就是外循环运行 |v|– 1倍。这个想法是,假设没有负权重周期,如果我们计算了最多i条边的最短路径,那么对所有边的迭代保证给出最多(i + 1)条边的最短路径 示例 让我们通过下面的示例图来理解算法。 设给定的源顶点为 0。将所有距离初始化为无穷大,但到源本身的距离除外。图中的顶点总数为 5,因此必须处理所有边 4 次。
让所有边按以下顺序进行处理:(B, E)、(D、 B)、(B、 D)、(A、 B)、(A、C)、(D、C)、(B、C)、(E、D)。当第一次处理所有边时,我们得到以下距离。第一行显示初始距离。第二行显示处理边 (B、 E)、(D、 B)、(B、 D) 和 (A、 B) 时的距离。第三行显示处理 (A, C) 时的距离。第四行显示处理 (D、 C)、(B、 C) 和 (E、 D) 的时间。
第一次迭代保证给出所有最短路径,这些路径的长度最多为 1 条边。当第二次处理所有边时,我们得到以下距离(最后一行显示最终值)。
第二次迭代保证给出所有最短路径,这些路径的长度最多为 2 条边。该算法再处理所有边缘 2 次。距离在第二次迭代后最小化,因此第三次和第四次迭代不会更新距离。
算法的实现
golang
type Edge struct {
startVertex int
endVertex int
weight int
}
// F 代表两点之间不可达
const F = 10000
func bellmanFord(graph [][]int, source int) []int {
edges := make([]Edge, 0)
n := len(graph)
//邻接矩阵转换为边表示
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
if graph[i][j] != F {
edges = append(edges, Edge{i, j, graph[i][j]})
}
}
}
dist := make([]int, n)
for i := 0; i < n; i++ {
dist[i] = F
}
dist[source] = 0
for i := 1; i < n; i++ {
for _, edge := range edges {
start := edge.startVertex
end := edge.endVertex
if dist[start] != F && dist[end] > dist[start]+edge.weight {
dist[end] = dist[start] + edge.weight
}
}
}
for _, edge := range edges {
start := edge.startVertex
end := edge.endVertex
if dist[start] != F && dist[end] > dist[start]+edge.weight {
fmt.Println("Graph contains negative weight cycle")
return []int{}
}
}
return dist
}
Java
package graph.bellman_ford;
import lombok.Data;
public class Graph {
private final int vertexCount;
private final int edgeCount;
private final Edge[] edge;
public Graph(int vertexCount, int edgeCount, Edge[] edge) {
this.vertexCount = vertexCount;
this.edgeCount = edgeCount;
this.edge = edge;
}