算法导论 | 第25章 所有结点对的最短路径问题


零、前言

前面讲了单源最短路径问题,指定一个原点一个终点,找到最短路径。但是如果我们要求所有结点对呢?

方案一:可以对每一个结点调用一次单源最短路径算法,一共调用|V|次。(每指定一个原点,可以求出其他任何点到该原点的举例)

对于权值为非负的图,可以调用Dijkstra算法,不同的优先队列实现得到不同的时间复杂度:①线性数组,O(V^3 + VE) = O(V^3)。②二叉堆,O(VElgV),在稀疏图的情况下是一个较大的改进。③斐波那契堆,O(V^2lgV + VE)。

如果有权重为负的边,就要使用Bellman-Ford算法。时间复杂度为O(V^2E),在稀疏图的情况下,时间为O(V^4)。


显然上述方法时间复杂度较大,我们可以利用Floyd-Warshall算法和Johnson算法(用于稀疏图)来求解。

Floyd-Warshall算法用邻接矩阵表示,Johnson算法用邻接表表示。


一、表示方式

1、权重的表示:


2、返回的是一个矩阵,dij表示从i结点到j结点的最短路径权重。注意,dij和dji不一样。为了能求出最短路径,我们还要保存父节点信息,pij表示从i结点到j结点时,j的父节点的位置。那么到时候通过修改22章的PRINT-PATH算法就能得到想要的结果。

算法导论 | 第25章 所有结点对的最短路径问题_第1张图片


3、最短路径和矩阵乘法

(1)利用动态规划来分析,lij(m)为在结点i和结点j之间最多有m条边的最小权重。而求lij(m)的时候利用lij(m-1)和在其中任意选一点k得到两段权值之和的最小值进行比较,取最小值,即



那么对于n个结点来说,求得lij(n-1)就已经达到了最优化,因此有


(2)自底向上实现

伪代码

算法导论 | 第25章 所有结点对的最短路径问题_第2张图片


通过对比矩阵乘法

算法导论 | 第25章 所有结点对的最短路径问题_第3张图片


两者结构很相似,因此我们对最短路径求解的时候,可以按照矩阵的方程来求解:L(n-1) = W(n-1)。主循环为:

算法导论 | 第25章 所有结点对的最短路径问题_第4张图片

时间复杂度为O(n^4)。


------->优化

对于矩阵或者数的n次方来说,可以通过重复平方的方法来讲时间复杂度将为O(n^3 logN)。

又因为有公式


可以不用考虑越界问题,于是得到代码如下:

算法导论 | 第25章 所有结点对的最短路径问题_第5张图片



二、Floyd-Warshall算法

这里使用的动态算法不是上面提到的那种,可以讲时间复杂度压缩到O(V^3)。上面的是按照从i到j的边的个数来递归定义的。这里是按照中间点的个数来定义的,其实质也差不多,但是递归的方式发生了变化。

从顶点vi到vj有一条长度为arcs[i][j]的路径,但不一定是最短的,需要经过n次试探,中间节点依次从v0、v1....vn-1增加,步骤如下:

(1)先考察路径(vi,v0,vj)师傅存在,如果存在比较(vi,vj)和(vi,v0)+(vi,vj)的大小,取最小值为,中间节点序号不大于0的最短路径。

(2)再增加一个节点v1,判断路径(vi...v1)和(v1...vj)是否存在,注意,vi到v1和v1到vj之间的节点序号最多只有v0一个,然后将二者的和与刚求得的新的(vi,vj)比较,取最小值更新。

(3)依次增加,比如增加节点k那么判断(vi....vk)+(vk...vj)之和的路径值与最新的(vi,vj)比较取最小值更新。注意(vi....vk)和(vk...vj)之间的节点取值序号必须在0-- k-1之间。

算法导论 | 第25章 所有结点对的最短路径问题_第6张图片

递归公式为:


FLOYD-WARSHALL(W)
{
    n = W.rows;
    D0 = W;
    for(k = 1;k <= n;++k){
        let Dk = (dij(k))be a new n*n matrix;
        for i = 1 to n
            for j = 1 to n
                dij(k) = min(dij(k-1),dik(k-1)+dkj(k-1) )
    }
    return Dn;
}

构建一条最短路径

算法导论 | 第25章 所有结点对的最短路径问题_第7张图片

算法导论 | 第25章 所有结点对的最短路径问题_第8张图片



三、用于稀疏图的Johnson算法

没看太懂,详细参考算法导论P409





你可能感兴趣的:(算法导论)