【算法】所有结点对的最短路径问题

概要

  • 可以通过对每个结点运行一次上一章中的最短路径算法,来解决所有结点对的最短路径问题,但效率较低,本章考虑的是用新的算法更高效的解决此问题
  • 本章使用邻接矩阵来表示图,边的权重也存储在一个矩阵 W 中,算法的输出存储在矩阵 D 中,还有一个精妙的前驱结点矩阵用于存放路径结果
  • 前驱结点矩阵的位置 ij 中存储的是,由 i 到 j 的一条最短路径中,j 的前驱结点
  • 由前驱结点矩阵的第 i 行可以诱导出,由结点 i 出发的最短路径树

最短路径和矩阵乘法

这一节是使用动态规划的方法解决所有结点对的最短路径问题,按动态规划的设计方法,有四个步骤

步骤一:分析最优解结构

  • 最优解必定是由子问题的最优解构成的,让本章的问题符合这一性质的是引理 24.1,一条最短路径的所有子路径都是最短路径

步骤二:所有结点对最短路径的递归解

  • 这一段的分析过程基本没看明白,主要的目的是给出一个递归式,指明由子问题的解如何组成问题的解
  • 公式给了两个,第一个公式 25.2 指出了如何由子问题的解组成问题的解,第二个公式 25.3 结合路径权重的场景具体分析,指出大于 n-1 条边的权重矩阵中的权重值都等于 n-1 条边的权重矩阵

步骤三:自底向上计算最短路径权重

  • 计算的算法并不出奇,三层 for 循环遍历,利用公式 25.2 进行递归计算(递归应该算作第四次循环)
  • 神奇的是这里计算的时候将公式 25.2 转成了矩阵乘法(没明白是如何转换的,数学功力不够),转成矩阵乘法之后算法的表述就变得更简单了,里面的三层 for 循环就是矩阵相乘的过程,外面的递归变成了不断的与同一个矩阵做乘法

步骤三改进:重复平方

  • 结合路径权重的场景分析,我们只需要最终的权重矩阵,对计算的中间结果不感兴趣,利用 25.3 给出的性质,递归计算改为平方计算,即中间结果的矩阵与自己相乘,这样找出一个大于 n-1 阶的结果即可
  • 这个改进可以将递归中的 O(n) 优化到 O(lgn)

步骤四:构建最优解

  • 书中没有给出这一步骤,我思考就是利用概要中给出的方法,由前驱结点矩阵诱导出最短路径树

Floyd-Warshall 算法

  • Floyd-Warshall 算法的运行时间为 V 的三次方

Floyd-Warshall 算法也是基于动态规划的思想,但是是从另一个角度思考问题,下面的结构还是按照动态规划的思路组织的

最短路径结构(步骤一)

  • Floyd-Warshall 算法考虑的是一条最短路径上的中间结点(实话说从这句话里完全看不清算法的思路)
  • 据我的理解,这里的问题和子问题分别是:
    【问题】结点 i 到结点 j 的一条包含的中间结点全部取自 1 ~ k 号结点的最短路径
    【子问题】结点 i 到结点 j 的一条包含的中间结点为全部取自 1 ~ k-1 号结点的最短路径
    通俗一点讲就是,i 到 j 的路径的中间结点只能从 1 ~ k(或者 k-1)号结点中选择,所形成的最短路径

所有结点对最短路径问题的一个递归解(步骤二)

  • 给出的递归解是这样的:关于问题 i 到 j 的最短路径,k 阶的 d 表示中间结点全部取自 1 ~ k 号结点的最短路径的权重,递归解说明了如何从 k-1 阶的 d 计算 k 阶的 d,最后计算到 n 阶的 d 就是在整个图中 i 到 j 的最短路径权重

自底向上计算最短路径权重(步骤三)

  • 这一段比较好理解,按照递归式进行计算即可,算法非常紧凑,运行时间为 n 的三次方

构建一条最短路径(步骤四)

  • 按照概述中的描述,构建最短路径需要通过前驱矩阵进行诱导(生成最短路径树)
  • 那么问题就变成了如何构建前驱矩阵,书中说明了两种方法:
    【方法一】由上述算法的计算结果 D 矩阵(由 n 阶的 d 组成的矩阵),来构造前驱矩阵
    【方法二】在计算 D 矩阵的同时计算前驱矩阵,说是同时其实关联并不紧密,只是用到了 D 矩阵的中间结果,即 1 ~ n 阶的 D 矩阵

有向图的传递闭包

  • 传递闭包是这样定义的:定义带权重的有向图 G,如果 G 中的结点 i 到 j 存在可以到达的路径,则(i,j)为 G 的传递闭包中的一条边
  • 可以利用 Floyd-Warshall 算法计算图的传递闭包:给 G 的每条边赋予权重 1,运行 Floyd-Warshall 算法,结果矩阵 D 中小于 n 的 d 值就代表一条闭包中的边(或许书中的表述更易理解:如果存在一条 i 到 j 的路径,则有 d(ij)< n)
  • 还有另外一种方法,用逻辑或 & 逻辑与操作替代 Floyd-Warshall 算法中的 min 和 + 操作(至于为什么可以这样替换我已经放弃思考了,应该是一个不太复杂的数学转换),这样改造后的算法运行会更快,因为逻辑或 & 逻辑与操作要比算法运算更快
  • 我们可以利用 Floyd-Warshall 算法在 V 的三次方时间内计算出图的传递闭包

用于稀疏图的 Johnson 算法

  • Johnson 算法可以在 O(V²lgV+VE) 内找到所有结点对的最短路径,对于稀疏图,E 比较小,则 Johnson 算法性能优于本章的前两节的算法
  • Johnson 算法的思路是对每个结点运行一次 Dijkstra 算法
  • Dijkstra 算法要求权重都为非负值,所以这里可能需要(如果所有权重都为非负值则不需要)为每条边重新赋予一个非负值权重,再对带有新的权重的图运行 Dijkstra 算法。其中重新赋予权重要求,新的图的最短路径结果集与原图相同。

重新赋予权重来维持最短路径

  • 这一小段讲的是如何计算一组与原图的最短路径相同的新权重
  • 引理 25.1(重新赋予权重并不改变最短路径)中的公式 25.9 给出了新权重的计算方法,通过选择一个函数 h,利用公式 25.9 就能够计算出一组最短路径相同的新权重

通过重新赋值来生成非负权重

  • 这一小段讲的是如何选择一个合适的函数 h,使得通过公式 25.9 计算出来的新权重都为非负值
  • 选择 h 的方法为:向图 G 中添加一个新结点 s,定义 s 到其它所有结点的边的权重都为 0,然后计算由 s 结点出发的单源最短路径结果,函数 h(v) 定义为 s 到 v 的最短路径权重
  • 书中同时给出了这样选择函数 h 的正确性的证明,利用三角不等式,简单明了

计算所有结点对之间的最短路径

这一小段给出了 Johnson 算法的具体过程:

  • 先插入新结点 s 生成新的图 G‘ 并初始化
  • 运行 Bellman-Ford 算法得到 s 的单元最短路径结果,构造函数 h
  • 由函数 h 计算新的权重(非负值),形成新的权重矩阵
  • 对每个结点运行 Dijkstra 算法(使用新的权重矩阵),得到一个结果矩阵 D’
  • 由 D‘ 根据公式 25.9 反向计算出原图 G 的最短路径结果矩阵 D
  • 此外,如果还需要构建最短路径,则可以在 D’ 的计算过程中保留前驱矩阵(将 Dijkstra 算法中生成前驱子图记录为前驱矩阵中的一行),这个前驱矩阵也可以作为原图 G (旧的权重矩阵)的前驱矩阵

你可能感兴趣的:(【算法】所有结点对的最短路径问题)