1. 算法概述
Floyd算法(Floyd-Warshall算法)是一种解决所有节点对最短路径问题的动态规划算法。
核心思想:
-
通过三重循环逐步松弛所有可能的路径
-
动态更新距离矩阵
-
"中介节点"思想:考虑通过某个中间节点k是否能缩短i到j的距离
特点:
-
可以处理负权边(但不能处理负权环)
-
一次性求出所有节点之间的最短路径
-
实现简单直观
2. 算法适用条件
✔ 带权有向图或无向图
✔ 可以处理负权边(但不能有负权环)
✔ 稠密图效果较好
✖ 不适用于大规模稀疏图(时间复杂度高)
3. 算法步骤
初始化阶段
// 初始化距离矩阵
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(i == j) d[i][j] = 0; // 节点到自身的距离为0
else d[i][j] = INF; // 其他初始化为无穷大
// 输入边信息
while(m--) {
cin >> a >> b >> w;
d[a][b] = min(d[a][b], w); // 处理重边
// 如果是无向图:
// d[b][a] = min(d[b][a], w);
}
动态规划过程
for(int k = 1; k <= n; k++) // 中介节点
for(int i = 1; i <= n; i++) // 起点
for(int j = 1; j <= n; j++) // 终点
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
4. 时间复杂度分析
项目 | 复杂度 | 说明 |
---|---|---|
时间复杂度 | O(N³) | 三重循环 |
空间复杂度 | O(N²) | 需要存储距离矩阵 |
5. 代码模板(C++)
基础版本
const int N = 210, INF = 0x3f3f3f3f;
int d[N][N];
int n, m;
void floyd() {
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main() {
cin >> n >> m;
// 初始化
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(i == j) d[i][j] = 0;
else d[i][j] = INF;
// 读入边
while(m--) {
int a, b, w;
cin >> a >> b >> w;
d[a][b] = min(d[a][b], w);
// 如果是无向图:
// d[b][a] = min(d[b][a], w);
}
floyd();
// 查询
while(q--) {
int a, b;
cin >> a >> b;
if(d[a][b] > INF / 2) cout << "impossible" << endl;
else cout << d[a][b] << endl;
}
return 0;
}
记录路径版本
int path[N][N]; // 记录i到j的最短路径上j的前驱节点
void floyd_with_path() {
// 初始化path
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
path[i][j] = j; // 初始时直接连接
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(d[i][j] > d[i][k] + d[k][j]) {
d[i][j] = d[i][k] + d[k][j];
path[i][j] = path[i][k]; // 更新前驱
}
}
void print_path(int i, int j) {
if(i == j) {
cout << i;
return;
}
cout << i << "->";
print_path(path[i][j], j);
}
6. 常见问题
Q1:Floyd算法为什么能处理负权边?
-
算法会考虑所有可能的路径,包括经过负权边的路径
-
通过动态规划逐步更新最短距离
Q2:如何判断图中是否存在负权环?
-
检查距离矩阵中对角线元素(d[i][i])
-
如果存在d[i][i] < 0,说明存在经过i的负权环
Q3:Floyd算法的k为什么要放在最外层?
-
这是动态规划的阶段划分
-
确保在考虑k作为中介节点时,i到k和k到j的最短路径已经考虑了所有1..k-1的中介节点
7. 例题
8. 优化技巧
-
对于稀疏图可以先进行节点重编号
-
使用位运算优化传递闭包计算
-
并行化最内层循环(现代CPU支持)
9. 声明
以上笔记仅为学习该算法的学习笔记,如有错误请指出
总结:Floyd算法是解决所有节点对最短路径问题的经典算法,虽然时间复杂度较高,但在稠密图和小规模图上非常实用,且实现简单直观。理解其动态规划思想对掌握图论算法有重要意义。