【计算完全最短路径的Floyd算法】
(一).定义**
Floyd–Warshall(简称Floyd算法)是一种著名的解决任意两点间的最短路径(All Paris Shortest Paths,APSP)的一种算法,是一种在具有正或负边缘权重(但没有负周期)的加权图中找到最短路径的算法,同时也被用于计算有向图的传递闭包。算法的单个执行将找到所有顶点对之间的最短路径的长度(加权)。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
(二)算法原理*
Floyd算法是一种动态规划(Dynamic Programming)算法。Floyd算法还是一个非常简单的三重循环,而且纯粹的Floyd算法的循环体内的语句也十分简洁。我们的目标就是通过遍历寻找从i点到j点的最短路径。
(1).用数组dis[i][j]来记录i,j之间的最短距离。初始化a[i][j],若i=j则a[i][j]=0,若i,j之间有边连接则a[i][j]的值为该边的权值,否则a[i][j]的值为int型的最大值65535。
(2).对所以的k值从1到n,更新任意两点之间的最短距离,计算a[i][k]+a[k][j]的值,若小于a[i][j],则a[i][j]=a[i][k]+a[k][j],否则a[i][j]的值不变。这样一来,当我们遍历完所有节点k,a[i][j]中记录的便是i到j的最短路径的距离。
(三)算法过程
1.先创建了一个结构体MGraph,两个二维数组(一个二维数组a存放顶点间的关系数据(为邻接矩阵),另一个二维数组p为路径数组)。
2.如果图中有n个顶点和m条边,初始时邻接矩阵a[i][j]表示顶点i到顶点j的权值,当i=j则a[i][j]为0,当顶点i不能到顶点j则a[i][j]为int型最大值65535。初始时通过遍历将路径矩阵p中的值全部赋值成-1。
3.如果a[i][j]>a[i][k]+a[k][j](k表示i到j路径中要经过的顶点来获取最短路径),然后在用k的值赋值给p[i][j]来标记这个最短路径要经过的点,则表示路径不是最短,需要更新。否则a[i][j]j就表示i直接到j的路径是最短的,就不需要更新了。
4.重复步骤3更新出所有路径的中间点得到最终的权值矩阵和路径矩阵。再通过递归路径数组来得到两个顶点的最短路径和最小权值。
如图为例:
代码如下所示:
#include "iostream"
using namespace std;
#define MAXVEX 80
#define INF 65535
typedef struct
{
int a[MAXVEX][MAXVEX];//邻接矩阵
int p[MAXVEX][MAXVEX];//最短路径的中间点
int numNodes;//当前顶点
int numEdges;//当前的边数
int weight;//权值
}MGraph;
//把图转化成邻接矩阵
void CreateMGraph(MGraph *MG)
{
int i,j,w,n;
cout<<"请输入顶点数和边数:"<<endl;
cin>>MG->numNodes>>MG->numEdges;
for(i=0;i<MG->numNodes;i++)
{
for(j=0;j<MG->numNodes;j++)
{
MG->p[i][j]=-1;
}
}
for(i=0;i<MG->numNodes;i++)
{
for(j=0;j<MG->numNodes;j++)
{
if(i==j)
MG->a[i][j]=0;
else
MG->a[i][j]=INF;//领接矩阵初始化 i不能直接到j的直接赋值成INF最大的值65535
}
}
cout<<"请输入边(Vi,Vj)的起点的下标i,终点的下标j和权值w(矩阵下标都是从0开始):"<<endl;
for(n=0;n<MG->numEdges;n++)
{
cin>>i>>j>>w;
MG->a[i][j]=w;
}
cout<<"图转换成邻接矩阵为:"<<endl;
for(i=0;i<MG->numNodes;i++)
{
for(j=0;j<MG->numNodes;j++)
{
cout<<MG->a[i][j];
cout<<" ";
}
cout<<endl;
}
}
void Floyd(MGraph *MG)
{
int i,j,k;
for(i=0;i<MG->numNodes;i++)
{
for(j=0;j<MG->numNodes;j++)
{
for(k=0;k<MG->numNodes;k++)
{
if(k==i||j==i)continue;
else
{
if(MG->a[j][k]>MG->a[j][i]+MG->a[i][k])
{
MG->a[j][k]=MG->a[j][i]+MG->a[i][k];
MG->p[j][k]=i;//标记中间到达的点i并赋值
}
}
}
}
}
cout<<"最短矩阵的最终结果:"<<endl;
for(i=0;i<MG->numNodes;i++)
{
for(j=0;j<MG->numNodes;j++)
{
cout<<MG->a[i][j];
cout<<" ";
}
cout<<endl;
}
}
void GetPathMatrix(MGraph *MG)
{
int i,j;
for(i=0;i<MG->numNodes;i++)
{
for(j=0;j<MG->numNodes;j++)
{
cout<<MG->p[i][j]<<" ";
}
cout<<endl;
}
}
//递归找最短路径
void Path(MGraph *MG,int x,int y)
{
int r;
if(MG->p[x][y]!=-1)
{
r=MG->p[x][y];
Path(MG,x,r);//通过递归从后往前找标记点 直到找到最初的标记点
cout<<MG->p[x][y]<<"->";//显示出被标记的路径
}
else {
}
}
//得到权值
int GetPathWeight(MGraph *MG,int x,int y)
{
int sum;
sum=MG->a[x][y];
return sum;
}
int main()
{
int i,j;
int x,y;
int sum=0;
MGraph MG;
CreateMGraph(&MG);
Floyd(&MG);
cout<<"得到的路径矩阵为:"<<endl;
GetPathMatrix(&MG);
cout<<"请输入起点和终点:";
cin>>x>>y;
cout<<"输出的起点到终点的最短路径:"<<endl;
cout<<x<<"->";
Path(&MG,x,y);
cout<<y<<endl;
sum=GetPathWeight(&MG,x,y);
cout<<"最短路径的权值:";
cout<<sum<<endl;
system("pause");
return 0;
}
运行结果如下: