维基百科
作用:求解单源最短路径的一种算法
伪代码(摘自维基百科):
procedure BellmanFord(list vertices, list edges, vertex source)
// 该实现读入边和节点的列表,并向两个数组(distance和predecessor)中写入最短路径信息
// 步骤1:初始化图
for each vertex v in vertices:
if v is source then distance[v] := 0
else distance[v] := infinity
predecessor[v] := null
// 步骤2:重复对每一条边进行松弛操作
for i from 1 to size(vertices)-1:
for each edge (u, v) with weight w in edges:
if distance[u] + w < distance[v]:
distance[v] := distance[u] + w
predecessor[v] := u
// 步骤3:检查负权环
for each edge (u, v) with weight w in edges:
if distance[u] + w < distance[v]:
error "图包含了负权环"
对步骤1的分析:
将图初始化包括包括两个部分,一个部分是将初始点的距离初始化为0,另一个部分是将其他点到初始点的距离初始化为无穷。这个操作的作用体现在第二步中。
对步骤2的分析:
重复v-1次(v代表图中点的数目)对所有边的遍历,在遍历的过程中更新初始点到图中其他点的最短距离。这里可以来分析一下步骤1的作用。
假设有如上所述的一个图(图片取自此博客)
因为我们在步骤1中将处原点以外的所有点到原点的距离都初始化为了无穷,所以在第一次遍历边的时候,只有与原点直接相连的点才会被更新距离,原因是更新规则是:
if distance[u] + w < distance[v]:
distance[v] := distance[u] + w)
所以在第一次遍历边的时候被更新的只有B,C,D。
(图片取自此博客)
接下来进行第二次遍历:
(图片取自此博客)
这样多次更新下来,每个点到原点的距离都会被逐渐被更新为到原点的最小距离。
对步骤3的分析
负权边:权重为负数的边。
负权环:图上一个点到图上另一个点的一个环,环上权重和为负数。
步骤三的负权环检测用一个例子来理解
这里有四个点,所以需要对所有边遍历3次。
遍历次数 | B | C | D |
---|---|---|---|
第一次 | 1 | 2 | INF |
第二次 | 1 | 2 | 2 |
第三次 | 1 | 0 | 2 |
遍历完后我们来执行步骤三,按照步骤三的规则依次对所有直接连接的点对进行检测
| AB | AC | BD | DC | CB |
|—|
| NO | NO | NO | NO | YES |
可以发现,在检测CB点对的时候发现了负权环,这也与我们的实际结果相一致。
想法
为什么要将所有边遍历v-1次呢?我的看法是防止当图的结构是链式结构时,如果不检测v-1次的话,将不能求出原点到所有点的最短距离。
c++代码实现参考这篇博客