Floyd
这个算法核心的一句话:
D^(0)[v][w]=min{D^(-1)[v][w],D^(-1)[v][0]+D^[v][0]+D^(-1)[0][w]}
首先我们针对妄图准备两个矩阵D^(-1)和P^(-1),D^(-1)就是网图的邻接矩阵,P^(-1)初设为P[i][j]=j这样的矩阵,它主要用来存储路径。先给出网图的数值,和前面我写的Dijkstra中的数据是一样的https://www.cnblogs.com/Rysort/articles/9490547.html。
v0-v1 1 v0-v2 5 v1-v3 7 v1-v4 5 v1-v2 3 v2-v4 1
v2-v5 7 v3-v6 3 v3-v4 2 v4-v6 6 v4-v7 9 v4-v5 3
v5-v7 5 v6-v8 7 v6-v7 2 v7-v8 4
代码如下,注意因为是求所有顶点到所有顶点的最短路径,因此,Pathmatirx和ShortPathTable都是二维数组。
typedef int Pathmatirx[MAXVEX][MAXVEX]; typedef int ShortPathTable[MAXVEX][MAXVEX]; //Floyd算法,求网图G中各顶点v到其余顶点w最短路径p[v][w]及带权长度D[v][w]
1 void ShortestPath_Floyd(MGraph G,Pathmatirx *P,ShortPathTable *D) 2 { 3 int v,w,k; 4 for(v=0;v<G.numVertexes;v++)//初始化D与F 5 { 6 for(w=0;w<G.numVertexes;w++) 7 { 8 (*D)[v][w]=G.matirx[v][w];//D[v][w]值即为对应点间的权值 9 (*P)[v][w]=w;//初始化P 10 } 11 } 12 for(k=0;k<G.numVertexes;k++) 13 { 14 for(v=0;v<G.numVertexes;v++) 15 { 16 for(w=0;w<G.numVertexes;w++) 17 { 18 if((*D)[v][w]>(*D)[v][k]+(*D)[k][w]) 19 {//如果经过下标为k顶点路径比原来两点间路径更短,将当前两点间权值设为更小的一个 20 (*D)[v][w]=(*D)[v][k]+(*D)[k][w]; 21 (*P)[v][w]=(*P)[v][k];//路径设置经过下标为k的顶点 22 } 23 } 24 } 25 } 26 }
- 程序开始运行,第4~11行就是初始化了D和P,使得它们称为D、P两个矩阵。从矩阵也得到,V0->V1路径权值是1,V0->V2路径权值是5,V0->V3无边连线,所以路径权值为最大值65535。
- 第12~25行,是算法的主循环,一共三层嵌套,k代表的就是中转顶点的下标。V代表起始顶点,w代表结束顶点。
- 当K=0时,也就是所有的顶点都经过V0中转,计算是否有最短路径的变化。可惜结果是,没有任何变化。
- 当K=1时,也就是所有的顶点都经过V1中转。此时,当V=0时,原本D[0][2]=5,现在由于D[0][1]+D[1][2]=4。因此有代码的第20行,二者取其最小值,得到D[0][2]=4,同理可得D[0][3]=8,D[0][4]=6,当V=2、3、4时,也修改了一些数据。由于这些最小权值的修改,所以在路径矩阵P上,也要作处理,将他们都改为当前的P[V][K]值。
- 接下来就是k=2一直到8结束,表示针对每个顶点做中转得到的计算结果,当然,我们也要清楚,D^(0)是以D^(-1)为基础,D^1是以D^0为基础,……,D^8是以D^7为基础,它们是有联系的,路径矩阵P也是如此。
至此,最短路径就算是完成了,你可以看到矩阵第V0行的数值与迪杰斯特拉(Dijkstra)算法求得的D数组的数值是完全相同,都是{0,1,4,7,5,8,10,12,16}。而且这里是所有顶点的最短路径权值和都可以计算出。
那么如何由P这个路径数组得出具体的最短路径呢?以V0到V8为例,P[0][8]=1,得到要经过顶点V1,然后将1取代0得到P[1][8]=2,说明要经过V2,然后将2取代1得到P[2][8]=4,说明要经过V4,然后将4取代2得到P[4][8]=3,说明要经过V3,……,这样很容易就推导出最终的最短路径为V0->V1->V2->V4->V3->V6->V7->V8。
求最短路径的显示代码可以这样写。
for(v=0;v<G.numVertexes;v++) { fow(w=v+1;w<G.numVertexes;w++) { printf("v%d-v%d wight: %d ",v,w,D[v][w]); k=P[v][w];//获得第一个路径顶点下标 printf(" path: %d",v);//打印源点 while(k!=w)//如果路径顶点下标不是终点 { printf(" -> %d",k);//打印路径顶点 k=P[k][w];//获得下一个路径顶点下标 } printf(" -> %d\n",w);//打印终点 } printf("\n"); }
再次回过头来看看弗洛伊德(Floyd)算法,它就是一个二重循环初始化加一个三重循环权值修正,就完成了所有顶点到所有顶点的最短路径计算。因此是O(n^3)时间复杂度。如果你面临需求所有顶点至所有顶点的最短路径问题时,弗洛伊德(Floyd)算法应该是不错的选择。
对求最短路径的两个算法(Dijkstra和Floyd)距离都是无向图,但它们对有向图依然有效,因为二者的差异仅仅是邻接矩阵是否对称而已。