回顾
迪杰斯特拉算法不能处理负的边权
Bellman-Ford
Bellman-Ford
算法可以解决负权边。举例理解:
假设边给出的顺序是:
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
- 设
dis[v]
为s到v的最短距离,则初始时dis
数组如下图所示:
第一次松弛:
- 当加入2->3这条边时,
dis[2] + 2 = ∞ + 2
仍然为∞,故不更新dis数组 - 当加入1->2这条边时,
dis[1] + (-3) = 0 + (-3) = -3 < ∞
,故1->2的距离更新为-3,更新dis[2] = -3
- 当加入1->5这条边时,
dis[1] + 5 = 0 + 5 < ∞
,1->5的距离更新为5,更新dis[5] = 5
- 当加入4->5这条边时,
dis[4] + 2 = 无穷 + 2 > 5
,1->5的距离不变,故不需更新dis[5]
- 最后,当加入3->4这条边时,
dis[3] + 3 = 无穷 + 3
,不更新dis[4]
- 以上为第一轮松弛
第二次松弛:
- 当再次加入2->3这条边时,
dis[2] + 2 = -3 + 2 = -1 < ∞
,故更新dis[3] = -1
- 当再次加入1->2这条边时,
dis[1] + (-3) = 0 + (-3) = -3 = dis[2]
,故不需要更新 - 当再次加入1->5这条边时,
dis[1] + 5 = 0 + 5 = 5 = dis[5]
,故不需要更新 - 当再次加入4->5这条边时,
dis[4] + 2 = ∞ + 2 > dis[4]
,故不需要更新 - 最后,当再次加入3->4这条边时,
dis[3] + 3 = -1 + 3 = 2 < dis[4]
,故不需要更新dis[4] = 2
第三次松弛:
-
当第三次加入2->3这条边时,
dis[2] + 2 = -3 + 2 = -1 = dis[3]
,故不需更新 -
当第三次加入1->2这条边时,
dis[1] + (-3) = 0 + (-3) = -3 = dis[2]
,故不需要更新 -
当第三次加入1->5这条边时,
dis[1] + 5 = 0 + 5 = 5 = dis[5]
,故不需要更新 -
当第三次加入4->5这条边时,
dis[4] + 2 = 2 + 2 = 4 < dis[5]
,故更新dis[5] = 4
-
最后,当第三次加入3->4这条边时,
dis[3] + 3 = -1 + 3 = 2 = dis[4]
,故不需要更新
第三次松弛: 不会更新dis数组
结论:至多经过n - 1次(上个例子只更新了n - 2次)之后,dis数组会更新为下图:
- 对于n个结点的图,需要松弛多少遍呢?至多
n - 1
遍,因为一条最短路径的长度最多为n - 1
条边。在实际操作中,该算法经常会在未达到n - 1
轮松弛前就已经计算出最短路径。鉴于此就有优化的方法 ——SPFA
算法。
算法模板
void Bellman-ford(int s)//求出源点s到其余各点的最短距离
{
memset(dis,0x3f,sizeof dis);//初始化距离为∞
dis[s] = 0;
for (int i = 1;i <= n - 1;i++)//松弛n - 1遍
{
int check = 0;
for (int j = 1;j <= m;j++)//枚举每一条边
{
//判断能否通过加入j这条边,使得到达v[j]号顶点的距离变短
//u[j]是j的起点,v[j]是j的终点,w[j]是j边的长度
if (dis[v[j]] > dis[u[j]] + w[j])
{
dis[v[j]] = dis[u[j]] + w[j];
check = 1;
}
}
//循环之后发现没有更新dis数组,则可以提前结束循环
if (check == 0) break;
}
}
算法总结
时间复杂度:O(NE),N是顶点数,E是边数
算法实现:设s为起点,dis[v]
即为s到v的最短距离,pre[v]
为v前驱。w[j]
是边j
的长度,且j
连接u、v
。初始化:dis[s] = 0,dis[v] = ∞(v≠s),pre[s] = 0
伪代码:
For (i = 1; i <= n - 1; i++)
For (j = 1; j <= m; j++)//枚举所有边
if (dis[u[j]] + w[j] < dis[v[j]])//u[j]、v[j]分别是这条边连接的两个起点与终点
{
dis[v[j]] = dis[u[j]] + w[j];
pre[v[j]] = u[j];
}
虽然Bellman-Ford算法可以求出存在负边权情况下的最短路径,却无法解决存在负权回路的情况。负权回路是指边权之和为负数的一条回路,上图中②-④-⑤-③-②这条回路的边权之和为-3。在有负权回路的情况下,从1到6的最短路径是多少?答案是无穷小,因为我们可以绕这条负权回路走无数圈,每走一圈路径值就减去3,最终达到无穷小。所以说存在负权回路的图无法求出最短路径,Bellman-Ford算法可以在有负权回路的情况下输出错误提示