任意点间求最短路径——弗洛伊德(Floyd)算法

迪杰斯特拉算法求得是 从某个特定的点出发到任意点之间的最短路径,但我们要处理从任一点到任一点怎么办?因此就直接介绍更具普遍意义而且代码写的很优美的弗洛伊德算法。
弗洛伊德算法求的是任意点间的最短路径,为了方便说明,这里先给出一个例子:
在这里插入图片描述
左边是一个网,右边的带回再解释。我们先来分析这个网,从v1到v2最短的路径是多少?反正不会是5,只要稍微绕个路,先去v0,再去v2,这个权值加起来只有3,所以最短路径应该是3(不能围成三角形的我想说明一下,权值不等同于这条线的长度,只是我们将这个边赋了一个值而已)。
现在来解释右边的四个矩阵。我们定义一个最短路径权值和的矩阵D,还定义一个对应顶点的最小路径的前驱矩阵P。最初的状态,我们记做D-1,其矩阵相当于是这个网的邻接矩阵。首先查看
D-1 [1][0] + D-1 [0][2] = 2 + 1 = 3 < D-1 [1][2] = 5,所以替换成 D-1 [1][2] = 3,由于对称,所以 D-1 [2][1] = 3,修改后记为D0。同时,P-1矩阵的在D-1修改的地方修改自己的前驱,说简单点就是中转站,于是P-1 [1][2] = 0,P-1 [2][1] = 0,记做P0
下面我们来看一个较为复杂的网(下面的网):
在这里插入图片描述
这是我们要来看的例子。其代码如下:

#define MAXVEX 20
#define INFINITY 65535

typedef struct
{
	int vexs[MAXVEX];
	int arc[MAXVEX][MAXVEX];
	int numVertexes, numEdges;
}MGraph;

typedef int Patharc[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];

/*弗洛伊德(Floyd)算法*/
void ShortestPath_Floyd(MGraph G, Patharc *P, ShortPathTable *D)
{    
	int v,w,k;    
	for(v=0; v<G.numVertexes; ++v)          // 初始化D与P 
	{        
		for(w=0; w<G.numVertexes; ++w)  
		{
			(*D)[v][w]=G.arc[v][w];	        // D[v][w]值即为对应点间的权值 
			(*P)[v][w]=w;				    // 初始化P 
		}
	}
	for(k=0; k<G.numVertexes; ++k)   
	{
		for(v=0; v<G.numVertexes; ++v)  
		{        
			for(w=0; w<G.numVertexes; ++w)    
			{
				if ((*D)[v][w]>(*D)[v][k]+(*D)[k][w])    // 如果经过下标为k顶点路径比原两点间路径更短
				{                                                         
					(*D)[v][w]=(*D)[v][k]+(*D)[k][w];      // 将当前两点间权值设为更小的一个 
					(*P)[v][w]=(*P)[v][k];          // 路径设置为经过下标为k的顶点 
				}
			}
		}
	}
}

假设我们的结构体里面的arc数组已经放好了,就像D-1一样,我i们主要来看弗洛伊德算法。最初的for循环就是初始化D-1和P-1两个数组(如上图下面的两个数组)。
下面就到了三个for循环的嵌套:

for(k=0; k<G.numVertexes; ++k)   
{
	for(v=0; v<G.numVertexes; ++v)  
	{        
		for(w=0; w<G.numVertexes; ++w)    
		{
			if ((*D)[v][w]>(*D)[v][k]+(*D)[k][w])    // 如果经过下标为k顶点路径比原两点间路径更短
			{                                                         
				(*D)[v][w]=(*D)[v][k]+(*D)[k][w];      // 将当前两点间权值设为更小的一个 
				(*P)[v][w]=(*P)[v][k];          // 路径设置为经过下标为k的顶点 
			}
		}
	}
}

第一个for循环,k是中转点,也就是说我去哪个点都要向Vk点先走。第二个for循环的变量v是起点,第三个是终点,这两个for循环就形成了任一点间路径的计算。我们来过一遍。第一遍,k是0,大家都要经过v0中转,可惜没有变化,记作D0和P0
在这里插入图片描述
结束了k = 0,来看k = 1,也就是都经过v1中转,D0 [0][1] + D0 [1][2] = 1 + 3 = 4 < D0 [0][2] = 5,取最小值,于是就将
D0 [0][2]改成了4,相应的,P0中的前驱也要改变,改成1。其他的顶点也都是一样的,修正后的已经用红圈标出:
在这里插入图片描述
接下来K = 2,即每个点都经过v2,可以看看是不是近一些,近的化就修改,相应的改变P矩阵中的前驱,这样循环到最后,我们就得到如下的修正后的结果:
在这里插入图片描述
这里我们可以从D8,P8这两个矩阵中看到每一行都是迪杰特斯拉算法,比如v0行,就是v0到任意点的路径的权值(D矩阵)以及路径(P矩阵),这样类推,所以弗洛伊德算法就实现了任意点间最短路径。主要是理解它的思想,它做到了用简洁的三个for循环嵌套实现了该需求,简洁明了。可惜的是由于它是三层for嵌套,其时间复杂度是O(n3)级别的。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值