Floyd算法
本文主要讲解的是Floyd算法,这同样是经典的最短路算法之一。不同的是,如果要求出一个图上任意两点间的最短路径,我们无需调用n次Dijkstra算法,这时Floyd算法便有了用武之地。另外,该算法同样适用于存在负权的情况。
Floyd算法本质上是动态规划,核心是动态转移方程,重点在于分析与推导。下面我们就来分析问题。从点i到点j的最短路径不外乎有两种可能:第一种是直接从i到j;第二种是从点i经过若干个结点再到点j。因此,我们对于每个结点k,检查是否有d(i, k)+d(k, j)
<
<script type="math/tex" id="MathJax-Element-7"><</script>d(i, j)成立,若成立则更新d(i, j)=d(i, k)+d(k, j) (即边的松弛操作)。这样,当我们遍历完所有的结点k时,得到的d(i, j)即为从点i到点j的最短距离。
不难写出状态转移方程:d[i][j] = min(d[i][j], d[i][k] + d[k][j]),用三重循环即可轻松地写出代码。给出核心代码如下:
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(d[i][j] < INF && d[k][j] < INF)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
调用上述代码之前,只需做一点简单的初始化:d[i][i]=0,其他d值均为正无穷。很显然,Floyd算法的时间复杂度为O(n^3)。它可以方便地求出图中任意两点的最短路,尤其是对于稠密图的效果更佳。但是,由于很高的时间复杂度,在n较大时不适合使用。
此外,我们在实际应用中,有时并不关心路径的长度,而是只关心每两点间是否存在通路。用true和false表示是否连通,则我们需要调整一下初始化,并把“d[i][j] = min(d[i][j], d[i][k] + d[k][j])”改为“d[i][j] = d[i][j] || (d[i][k] && d[k][j])”。这样得出的结果称为传递闭包(Transitive Closure)。