七、最短路径——弗洛伊德(Floyd)算法

        为了能讲明白弗洛伊德(Floyd)算法的精妙所在,我们先来看最简单的案例。下图是一个最简单的3个顶点连通网图。

        我们先定义两个二维数组D[3][3]和P[3][3],D代表顶点到顶点的最短路径权值和的矩阵。P代表对应顶点的最小路径的前驱矩阵。在未分析任何顶点之前,我们将D命名为D^{-1},其实它就是初始的图的邻接矩阵。将P命名为P^{-1},初始化为图中所示的矩阵。

                

        首先我们来分析,所有的顶点经过v0后到达另一顶点的最短路径。因为只有三个顶点,因此需要查看v1→v0→v2,得到 D^{-1}[1][0] + D^{-1}[0][2] = 2+1 = 3。D^{-1}[1][2] 表示的是v1→v2的权值为5,我们发现 D^{-1}[1][2] > D^{-1}[1][0] + D1[0][2],通俗的话讲就是v1→v0→v2比直接v1→v2距离还要近。所以我们就让 D^{-1}[1][2] = D^{-1}[1][0] + D^{-1}[0][2] = 3,同样的 D^{-1}[2][1] = 3,于是就有了D^{0}的矩阵。因为有变化,所以P矩阵对应的 P^{-1}[1][2] 和 P^{-1}[2][1] 也修改为当前中转的顶点v0的下标0,于是就有了P^{0}。也就是说    D^{0}[v][w] = min\left \{ D^{-1}[v][w],D^{-1}[v][0]+D^{-1}[0][w]\right \},此时的D^{0}和 P^{0}分别是

        接下来,其实也就是在D^{0}P^{0}的基础上继续处理所有顶点经过v1和v2后到达另一顶点的最短路径,得到D^{1}P^{1}D^{2}P^{2}完成所有顶点到所有顶点的最短路径计算工作。 

        现在我们来看一下弗洛伊德(Floyd)算法的具体实现,我们先给出下图所示的网以及它的D^{-1}P^{-1}矩阵:

D代表顶点到顶点的最短路径权值和的矩阵。P代表对应顶点的最小路径的前驱矩阵。

扫描二维码关注公众号,回复: 14436935 查看本文章

 具体实现:

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

#define MAXVEX 100//最大顶点数
typedef char VertexType;//顶点类型
typedef int EdgeType;//边上的权值类型
typedef struct
{
	VertexType vexs[MAXVEX];//顶点表
	EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵
	int numVertexte;//当前顶点数
	int numEdges;//当前边数
}MGraph;

void ShortestPath_Floyd(MGraph G, vector<vector<int>>& P, vector<vector<int>>& D)
{
	for (int i = 0; i < G.numVertexte; ++i)
	{
		for (int j = 0; j < G.numVertexte; ++j)
		{
			D[i][j] = G.arc[i][j];//用G的邻接矩阵初始化D
			P[i][j] = j;
		}

		for (int i = 0; i < G.numVertexte; ++i)
		{
			for (int j = 0; j < G.numVertexte; ++j)
			{
				for (int w = 0; w < G.numVertexte; ++w)
				{
					if (D[j][w] > D[j][i] + D[i][w])//如果经过下标为i的顶点路径比原两点间路径更短
					{
						D[j][w] = D[j][i] + D[i][w];//更新D的值
						P[j][w] = P[j][i];//更新P的值
					}
				}
			}
		}
	}
}

/*我们可以通过下面这个函数将最短路径打印出来*/
void Print(MGraph G, vector<vector<int>>& P, vector<vector<int>>& D)
{
	for (int i = 0; i < G.numVertexte; ++i)
	{
		for (int j = i+1; j < G.numVertexte; ++j)
		{
			printf("v%d-v%d weight:%d ", i, j, D[i][j]);//打印源点 终点  以及他们之前的权
			int k = P[i][j];//第一个路径顶点下标
			printf(" path:%d", i);//打印源点
			while (k != j)//如果没有到终点
			{
				printf("-> %d", k);//打印路径顶点
				k = P[k][j];//获取下一个路径顶点下标
			}
			printf(" -> %d\n", j);//打印终点
		}
		printf("\n");
	}
}

        最终我们得到的结果如下图所示:

        那么如何由P这个路径数组得出具体的最短路径呢?以v0到v8为例,从上图的右图第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。

       如果你面临需要求所有顶点至所有顶点的最短路径问题时,弗洛伊德(Floyd)算法应该是不错的选择。时间复杂度为O(n^{3})

        另外,我们虽然对求最短路径的两个算法举例都是无向图,但它们对有向图依然有效,因为二者的差异仅仅是邻接矩阵是否对称而已。

猜你喜欢

转载自blog.csdn.net/ThinPikachu/article/details/123678745