1.算法概述
Bellman–Ford 算法由 Richard Bellman 和 Lester Ford 在 1958 年提出,用于解决带权图的单源最短路径问题
注意
虽然spfa算法在各方面均优于bellman-Ford算法,但是有边数限制类最短路问题只能使用bellman-Ford算法
-
核心思想
-
反复对所有边进行「松弛」操作,最多做 |V|–1 轮即可找到最短路径;再做一次松弛即可检测负权回路。
-
特点:
-
可处理负权边
-
能检测「负权回路」
-
2.算法适用条件
-
任意带权有向图或无向图(将无向边视为两条相反方向的有向边)
-
权重可为负,但若存在负权回路则无最短解,只能检测并报告
3.算法步骤(邻接表/矩阵通用版)
// 假设 vertices = 顶点数 n,edges 为边列表 [(u,v,w), ...]
// dist[]、pre[] 全局或传参
// 初始化
for (int i = 1; i <= n; i++) {
dist[i] = INF;
pre[i] = -1;
}
dist[src] = 0;
// 松弛操作:最多做 n-1 轮
for (int i = 1; i < n; i++) {
for (auto &e : edges) {
int u = e.u, v = e.v, w = e.w;
if (dist[u] != INF && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pre[v] = u;
}
}
}
// 负权回路检测:再做一次松弛
for (auto &e : edges) {
int u = e.u, v = e.v, w = e.w;
if (dist[u] != INF && dist[u] + w < dist[v]) {
// 存在负权回路
return false;
}
}
return true; // 无负权回路,dist 中保存最短距离
4.复杂度
时间复杂度
实现方式 | 时间复杂度 | 适用场景 |
---|---|---|
边列表松弛 | O(V·E) | 中小规模任意图 |
邻接矩阵扫描 | O(V³) | V 很小且密集时可行 |
空间复杂度:O(V + E)
5.代码模板(C++)
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
struct Edge { int u, v, w; };
int n, m; // 顶点数 n,边数 m
vector<Edge> edges;
vector<int> dist, pre;
bool bellman_ford(int src) {
dist.assign(n+1, INF);
pre .assign(n+1, -1);
dist[src] = 0;
// 松弛 |V|-1 次
for (int i = 1; i < n; i++) {
bool updated = false;
for (auto &e : edges) {
if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v]) {
dist[e.v] = dist[e.u] + e.w;
pre[e.v] = e.u;
updated = true;
}
}
if (!updated) break; // 提前退出
}
// 负权回路检测
for (auto &e : edges) {
if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v])
return false;
}
return true;
}
int main() {
cin >> n >> m;
edges.resize(m);
for (int i = 0; i < m; i++)
cin >> edges[i].u >> edges[i].v >> edges[i].w;
if (!bellman_ford(1)) {
cout << "存在负权回路\n";
} else {
for (int i = 1; i <= n; i++)
cout << "1→" << i << " 最短距离 = "
<< (dist[i]==INF ? -1 : dist[i]) << "\n";
}
return 0;
}
6.常见问题
Q1:为什么要做 n-1 轮松弛?
因为任何一条最短路径最多经过 n-1 条边。
Q2:如何提前终止?
若某一轮松弛中未发生任何松弛(updated = false),可提前退出主循环。
Q3:如何输出具体路径?
使用 pre[]
数组从终点逆推到源点,再逆序打印即可。
Q4:能否用于无向图?
可以,将每条无向边 (u–v,w) 拆为两条有向边 (u→v,w)、(v→u,w)。
7.例题
-
AcWing 138. 单源最短路(Bellman–Ford)
8.声明
以上旨在总结 Bellman–Ford 算法要点,如有错误或不全,欢迎指正!
总结:
Bellman–Ford 算法通过反复松弛实现单源最短路径计算,能处理负权边并检测负权回路。
适用于顶点数较小、需考虑负权边或回路检测的场合。
理解松弛操作与负回路检测是掌握该算法的关键。