算法: Johnson 算法

Johnson 算法是用来解决在有负权重边图里的最短路径问题的,它主要了结合 Dijkstra 算法和 Bellman-Ford 算法。其实负数边的问题也可以用 Folyd 算法来解决,只不过它的算法复杂度是 ,而 Johnson 算法在稀疏图里复杂度是 ,会比 Folyd 好一点。

Reweight

我们考虑如下图结构

其中 0 -> 1 是负数的,是不能使用 Dijkstra 去求最短路径的。这时我们可能会想到把全部的边都加上 5 那大家不就都变成正数了?使用 Dijkstra 求完最短路径后再减回 5 那答案不就求到了么?这是一种思路,但是不完成正确,考虑如下图

这个图的 S 到 T 最短路径是 3(选上面那条路) ,但是所有边都加 1 后,最短路径就变成了 4 (选了下面那条路)。

而 Johnson 的方法是通过给每个节点设置一个值,用这些节点的值去做 reweight,公式如下:

就是节点 X 的值,这个值是通过 Bellman-Ford 求出来的。

h[x]

现在来说说怎么求这个 。其实很简单,在这个图中添加一个虚拟的节点,如下图所示,为什么说是虚拟的呢,因为用完就删除了。这个虚拟节点指向所有的节点,而指向所有节点的边权重为 0。

就是用 Bellman-ford 去求这个虚拟节点到每个节点的最短距离。以上图为例,每个节点的 h 值为:

节点 h[x]
0 0
1 -5
2 0
3 0

有了这些 值后就可以对每条边进行 reweight 操作了,最终的路径应该要减去开始节点的 h 值,再加上结束节点的 h 值。以 0 -> 1 -> 2 为例,Dijkstra 算法求出的路径是

然后再还原到真实的路径

以上面的图为例

Johnson 算法步骤

上面都是 Johnson 的关键步骤,下面就说说整个 Johnson 算法的流程。

  1. 添加虚拟节点到这个图里,并添加指向所有节点的虚拟边,这些边的权重为 0
  2. 以虚拟节点节点为起点,运行 Bellman-Ford 算法,求出到每个节点的最短距离,这些最短距离为每个节点的 值
  3. 用上面求出的 h 值去更新图里的边,使得
  4. 移除添加的虚拟节点和边
  5. 在每个节点运行 Dijkstra 计算到其它节点的最短距离

再来分析这个算法的时间复杂度,Bellman-Ford 算法时间复杂度是 ,Dijkstra 算法的时间复杂度是 ,因为每个节点都要做 Dijkstra,所以总的时间复杂度是 。

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