Floyd 算法(Floyd-Warshall algorithm)又称为弗洛伊德算法、插点法,用于在给定的加权图中计算两个顶点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。
Floyd 算法和 Dijkstra 算法对比话,Dijkstra 算法可以计算给定起始点到其他所有顶点的最短路径。Floyd 算法能够计算所有顶点到所有顶点的最短路径,可以用于查询任意两个顶点之间的最短路径。
Floyd 算法基本思想,是计算出 i->k + k->j 的距离是否比 i->j 的距离更近。i->j 表示两个顶点之间直接的距离,如果两个顶点有边则具有有限的距离,如果两个顶点没有边,初始时设置为无限远。i->k + k->j 表示 i、j 两个顶点经过 k 顶点后的距离。这个 k 顶点就像是插入到 i 和 j 之间的顶点,所以也叫做插点法。
算法维 2 个二维数组,数组 1 用于记录两点之间的最小权值,数组 2 用于记录 i j 经过 k 顶点距离变得更短。
接下来,我们实现 Floyd 算法计算 1 顶点和 5 顶点之间的最短路径,下面代码中最核心的代码是 46-67 行代码,三层嵌套循环计算了图中任意两个顶点经过 k 结点距离最短。
#include <iostream>
#include <list>
using namespace std;
#define M 65535
// 定义顶点
const int vertex_size = 6;
int vertex[] = { 1, 2, 3, 4, 5, 6 };
// 定义边
int edges[][6] = {
{ 0, 7, 9, M, M, 14},
{ 7, 0, 10, 15, M, M},
{ 9, 10, 0, 11, M, 2},
{ M, 15, 11, 0, 6, M},
{ M, M, M, 6, 0, 9},
{14, M, 2, M, 9, 0}
};
void floyd()
{
// 用于记录两个顶点之间的最小距离
int others_others[vertex_size][vertex_size] = { 0 };
for (int i = 0; i < vertex_size; ++i)
{
for (int j = 0; j < vertex_size; ++j)
{
others_others[i][j] = edges[i][j];
}
}
// 用于记录两个顶点经过 x 顶点为最小距离
int shortest_path[vertex_size][vertex_size] = { 0 };
for (int i = 0; i < vertex_size; ++i)
{
for (int j = 0; j < vertex_size; ++j)
{
shortest_path[i][j] = -1;
}
}
// 开始计算任意顶点经过 k 是否更近
for (int k = 0; k < vertex_size; ++k)
{
for (int i = 0; i < vertex_size; ++i)
{
for (int j = 0; j < vertex_size; ++j)
{
// 经过 k 之后的距离
int i_k_j = others_others[i][k] + others_others[k][j];
int i_j = others_others[i][j];
if (i_k_j < i_j)
{
// 记录 i 和 j 顶点经过 k 顶点距离更短
shortest_path[i][j] = k;
// 记录 i 和 j 之间更短的距离值
others_others[i][j] = i_k_j;
}
}
}
}
// 计算任意两个顶点之间的最短路径
list<int> path;
int start_vertex = 0;
int end_vertex = 4;
path.push_front(end_vertex);
while (true)
{
end_vertex = shortest_path[start_vertex][end_vertex];
if (end_vertex == -1)
{
path.push_front(start_vertex);
break;
}
path.push_front(end_vertex);
}
for (auto v : path) cout << v + 1 << " ";
cout << endl;
}
int main()
{
floyd();
return 0;
}
程序输出结果:
1 3 6 5
打印下 shortest_path 矩阵:
for (int i = 0; i < vertex_size; ++i)
{
for (int j = 0; j < vertex_size; ++j)
{
cout.width(3);
cout << shortest_path[i][j] << " ";
}
cout << endl;
}
-1 -1 -1 2 5 2
-1 -1 -1 -1 3 2
-1 -1 -1 -1 5 -1
2 -1 -1 -1 -1 2
5 3 5 -1 -1 -1
2 2 -1 2 -1 -1
每一行表示当前顶点到其他顶点的最短路径表示,Dijkstra 是不是和这个很像。-1 表示当前顶点到该顶点直接距离最近。如果是 2 的话,表示当前顶点到该顶点经过 2 顶点会更更近。
打印下用于记录任意两顶点距离的 ohters_others 矩阵:
0 7 9 20 20 11
7 0 10 15 21 12
9 10 0 11 11 2
20 15 11 0 6 13
20 21 11 6 0 9
11 12 2 13 9 0
如果想直接获得某两顶点之间距离,直接访问该矩阵即可。