Floyd算法解析

1、基本介绍

Floyd算法是用于求解带权有向图中任意两顶点间的最短路径,同时也被用于计算有向图的传递闭包
时间复杂度为O(n3),空间复杂度为O(n2)。

2、算法思想

在谈算法过程前,我们需要先大概理解它的思路:图中一个顶点到另一个顶点的路径认为有两种,第一种是直接连接,即从起始点连接到终点,路径长度为A [ i ] [ j ];第二种是间接连接,即通过中间点k将两者连接起来,路径长度为A [ i ] [ k ] + A [ k ] [ j ] 。而Floyd算法的任务即是比较这两种路径,选择出长度较短的路径

3、物理结构

根据算法思想,我们需要频繁比较两顶点间的权值,故采用邻接矩阵。同时,该算法需要建立两个二维数组,A数组记录两个顶点间的最短路径长度;path数组记录两个顶点间的中间点,因为在具体实现时,中间点可能不止一个,这里记录的中间点为终点的前一个顶点。

4、实例及代码

以如下所示图为例:
在这里插入图片描述
首先根据图中任意两个顶点间的最直接关系,建立A数组和Path数组。A数组记录两顶点间的路径长度(“∞”表示两个顶点间路径无穷大,即无路径),path数组记录终点的前一个顶点(“-1”表示终点前面无顶点,即两个顶点间无路径)。
在这里插入图片描述
因为两个顶点间的任意性以及中间点的未知性,显然的,我们需要使用循环,以使得任意两个顶点作为端点,任意一个顶点作为中间点。当找到间接路径小于直接路径时,改变A数组和path数组的值。
实现代码如下:
注意:这里的多重循环中,中间点 k 的循环必须在第一层循环,起点 i 和终点 j 的位置则可交换。当中间点 k 的循环在最后一层循环时,就是另一种结果了,具体到后面的实验总结解释。

	for ( k=0; k<N; k++ )		//N是顶点数量
		for ( i=0; i<N; i++ )
			for ( j=0; j<N; j++ )
				if ( A[i][j] > A[i][k] + A[k][j] )
				{	A[i][j] = A[i][k] + A[k][j];
					path[i][j] = path[k][j];
				}
  • (1) 我们先取0为中间点,遍历所有起点和终点,发现所有经过0的间接路径都没有直接路径短,即A [ i ] [ j ] < A [ i ] [ 0 ] + A [ 0 ] [ j ] ,因此A数组和path数组均不改变元素值。
  • (2) 现在再取1为中间点,发现有0→1→2路径长度为9,小于0→2的值(∞),即A [ 0 ] [ 2 ] > A [ 0 ] [ 1 ] + A [ 1 ] [ 2 ] ,此时将A[0][2]的值改为较短路径长度9,path[0][2]的值改为中间点1。
    在这里插入图片描述
  • (3) 同理,当2为中间点时有:
    原来的1→0(∞)变为1→2→0(7),此时将A[1][0]的值改为较短路径长度7,path[1][0]的值改为中间点2;
    同理原来的3→0(∞)变为3→2→0(4);原来的3→1(∞)变为3→2→1(4)。
    在这里插入图片描述
  • (4) 最后以3为中间点,出现更短路径的有:
    从0到2:由原来的路径0→1→2(9)变为0→3→2(8);此时将A[0][2]的值改为较短路径长度6,path[0][2]的值改为中间点3;
    从1到0:由原来的路径1→2→0(7)变为1→3→2→0(6),此时将A[][]的值改为较短路径长度6,path数组元素的值改为中间点3;
    从1到2:由原来的路径1→2(4)变为1→3→2(3)。
    在这里插入图片描述
    至此,任意顶点的最短路径全部计算完毕,A数组所示即为最短路径长度,path数组所示即为最短路径。

6、实验总结

(1)中间点K在第一层循环时

如下图所示,图中只显示部分顶点和路径。
当中间点k=0时浏览所有起点和终点,假设发现经过中间点0的较短路径如图1所示,其中有从5到3的路径5→0→3。
当k=1时又重新浏览了所有起点和终点,发现经过中间点1的较短路径如图2所示,其中有从4到6的路径4→1→6。
我们发现在前期的较短路径里,中间点都是只有一个的。
在这里插入图片描述在这里插入图片描述
当k=2时继续重新浏览所有起点和终点,建立了图3所示的路径,其中有从3到4的路径3→2→4。
而到这里我们发现5到6的路径已经出来了(图4),他经过了5个中间点(0、3、2、4、1),即到后期各个路径将会渐渐的连接在一起,从而形成多个中间点。
在这里插入图片描述在这里插入图片描述
不难发现,Floyd算法其实是在图的各个局部里寻找最短路径,并逐渐扩大每个局部的范围直至合为一体。这一点和Dijkstra算法相似,不同的地方在于Dijkstra算法是只从一个点,即一个局部出发扩展,而Floyd算法是从多个局部进行扩展。

(2)中间点K在最后一层循环为什么不行?

举个简单的例子:
在这里插入图片描述
上面的图中,显然的0到1的最短路径为0→3→2→1(6),但如果我们将中间点K的循环置于最后一层,即如下代码时,得到的结果为0→1(10)。

	for ( i=0; k<N; k++ )		//N是顶点数量
		for ( j=0; i<N; i++ )
			for ( k=0; j<N; j++ )	//k循环在最内层
				if ( A[i][j] > A[i][k] + A[k][j] )
				{	A[i][j] = A[i][k] + A[k][j];
					path[i][j] = path[k][j];
				}

原因很简单,当我们把 k 循环置于最后一层后,我们是先确定了起点和终点,然后再一个一个的寻找中间点。不计从0到0,我们第一次的循环是寻找从0到1的路径,此时中间点k在0、1、2、3中改变,中间点有且只能有一个,而图中的路径里中间点有两个,因此会判别失败,从0到1的路径仍然为0→1(10)。而问题就出现在:当循环结束后,这个路径的判断就此结束了,以后无论后面路径的判断如何,我们都不会再回过头来重新审视从0到1的路径。当k=2时我们找到了从3到1的路径3→2→1(5),但我们仍需在k=3时重新判断0→3→1和0→1的路径哪个更短,但因为我们不再重新审视从0到1的路径,因此也无法判断,无法更新。这也就是为什么我在“具体思路”里一直强调当中间点 k 改变时要重新浏览所有起点和终点的原因。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值