Floyd算法又称为,插点法,是一种用于寻找给定的加权图中多源点之间最短路径的算法。通过一个图的权值矩阵求出它的每两点间的最短路径矩阵。Floyd算法使用三个for循环就可以解决问题,所以它的时间复杂度为O(n^3)。但是要弄清楚三个循环的顺序。
从图的带权邻接矩阵A=[a(i,j)] n×n开始,递归地进行n次更新,即由矩阵D(0)=A,按一个公式,构造出矩阵D(1);又用同样地公式由D(1)构造出D(2);……;最后又用同样的公式由D(n-1)构造出矩阵D(n)。矩阵D(n)的i行j列元素便是i号顶点到j号顶点的最短路径长度,称D(n)为图的距离矩阵,同时还可引入一个后继节点矩阵path来记录两点间的最短路径。
过程如下:
1.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
2.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。
把图用邻接矩阵G表示出来,如果从Vi到Vj有路可达,则G[i,j]=d,d表示该路的长度;否则G[i,j]=无穷大。定义一个矩阵D用来记录所插入点的信息,D[i,j]表示从Vi到Vj需要经过的点,初始化D[i,j]=j。把各个顶点插入图中,比较插点后的距离与原来的距离,G[i,j] = min(G[i,j], G[i,k]+G[k,j]),如果G[i,j]的值变小,则D[i,j]=k。在G中包含有两点之间最短道路的信息,而在D中则包含了最短通路径的信息。
比如,要寻找从V5到V1的路径。根据D,假如D(5,1)=3则说明从V5到V1经过V3,路径为{V5,V3,V1},如果D(5,3)=3,说明V5与V3直接相连,如果D(3,1)=1,说明V3与V1直接相连。
然而,如下的循环顺序是错误的
for ( int i = 0; i < 节点个数; ++i )
{
for ( int j = 0; j < 节点个数; ++j )
{
for ( int k = 0; k < 节点个数; ++k )
{
if ( Dis[i][k] + Dis[k][j] < Dis[i][j] )
{
// 找到更短路径
Dis[i][j] = Dis[i][k] + Dis[k][j];
}
}
}
}
因为最短距离一共有三种情况,
1.两点的直达距离最短。
2.两点间只通过一个中间点而距离最短。
3.两点间用通过两各以上的顶点而距离最短。
上述循环方式对于第三种情况可能会出错。因为如果把检查所有节点X放在最内层,这样会过早的把i到j的最短路径确定下来了,而当后面存在更短的路径时,已经不再会更新了。比如下图的情形
所以,我们需要改写循环顺序,如下:
for ( int k = 0; k < 节点个数; ++k )
{
for ( int i = 0; i < 节点个数; ++i )
{
for ( int j = 0; j < 节点个数; ++j )
{
if ( Dis[i][k] + Dis[k][j] < Dis[i][j] )
{
// 找到更短路径
Dis[i][j] = Dis[i][k] + Dis[k][j];
}
}
}
}
这样一来,对于每一个节点X,我们都会把所有的i到j处理完毕后才继续检查下一个节点。
完整的code
#include<iostream>
const int Maxm=10000;
using namespace std;
int p, q, k, m;
int Vertex, Line[Maxm];
int Path[Maxm][Maxm], Dist[Maxm][Maxm];
void Root(int p, int q)
{
if (Path[p][q]>0)
{
Root(p, Path[p][q]);
Root(Path[p][q], q);
}
else
{
Line[k] = q; k++;
}
}
int main()
{
memset(Path, 0, sizeof(Path));
memset(Dist, 0, sizeof(Dist));
cin >> Vertex;
for (p = 0; p != Vertex; p++)
for (q = 0; q != Vertex; q++)
cin >> Dist[p][q];
for (k = 0; k != Vertex; k++)
for (p = 0; p != Vertex; p++)
if (Dist[p][k]>0)
for (q = 0; q != Vertex; q++)
if (Dist[k][q]>0)
{
if (((Dist[p][q]>Dist[p][k] + Dist[k][q]) || (Dist[p][q] == 0)) && (p != q))
{
Dist[p][q] = Dist[p][k] + Dist[k][q];
Path[p][q] = k;
}
}
for (p = 0; p != Vertex; p++)
{
for (q = p + 1; q <= Vertex; q++)
{
cout << "\n==========================\n";
cout << "Source:" << p << '\n' << "Target" << q << '\n';
cout << "Distance:" << Dist[p][q] << '\n';
cout << "Path:" << p; k = 2; Root(p, q);
for (m = 2; m <= k - 1; m++)
cout << "-->" << Line[m]; cout << '\n';
cout << "==========================\n";
}
}
return 0;
}