本周算法:有向无环图的最短路径
Importnew注:如果你也对Java技术翻译分享感兴趣,欢迎加入我们的Java开发小组。参与方式请查看小组简介。
介绍
我们已经知道了如何通过Dijkstra算法在非负权图中找到最短路径。即使图中有负权边,我们也知道通过Bellman-Ford算法找到一个从给定的源点到其它所有节点的最短路径。现在我们将看到一个在线性时间内运行得更快的算法,它可以在有向无环图中找到从一个给定的源点到其它所有可达顶点的最短路径,又名有向无环图(DAG)。
由于有向无环图无环所以我们不必担心负环的问题。正如我们已经知道在负环里讨论最短路径是毫无意义的一样,因为我们可以在这些环里不断“循环”,但实际上我们得到的路径将变得越来越短(构成负的环路,存在死循环)。
负环的存在使我们试图找到最短路径的行为变得毫无意义!
因此,我们要解决Dijkstra算法和的Bellman-Ford算法中的两个问题。首先,我们只需要非负的权重,其次,图中不能有环。好了,这样我们就可以通过这个算法处理满足这两种情况的例子了。
概述
关于有向无环图(DAGs)我们首先要知道,它们可以很容易地进行拓扑排序。拓扑排序应用范围非常之广,其中最常用的或许就是用于安排依赖任务(依赖任务是同属于一个工作中相同任务的实体,这些实体是保证互连的,它们解决共同的问题)。
拓扑排序通常是用来“排序”依赖任务的!
经过拓扑排序,我们最终会得到一张DAG图的顶点列表,我们确信在拓扑排序列表中如果存在一条边(u,v),那么顶点u会先于顶点v进入列表中。
如果有一条边(u,v),那么顶点u一定在顶点v前面。这个结果通过这张图片变得更加通俗易懂。其中B和D之间没有边,但在列表中B在D前面!
此信息异常重要,我们唯一需要做的事情就是通过这个排序列表来计算距离最短的路径,这和Dijkstra算法比较相似。
好了,让我们来总结一下这个算法:
-首先,我们必须对有向无环图进行拓扑排序;
-其次,我们将到源点的距离初始化为0并将到其它所有顶点的距离设置为无穷大;
-最后,对于列表中的每一个顶点,我们从它的所有邻节点中找到最短路径的那个顶点;
这很像Dijkstra算法,但是其主要区别是我们使用了一个优先级队列,并且此次我们使用的是经过拓扑排序的列表。
代码
此次的代码实际上是一段伪代码。虽然目前为止所有的例子都是用PHP编写,但是伪代码更易于理解,也不会将你与某一特定的语言实现绑定在一起。此外,如果你对给定的语言感到难以理解,那么对你而言读懂代码比阅读伪代码更加困难。
Topologically sort G into L; Set the distance to the source to 0; Set the distances to all other vertices to infinity; For each vertex u in L - Walk through all neighbors v of u; - If dist(v) > dist(u) + w(u, v) - Set dist(v) <- dist(u) + w(u, v);
应用
我们必须使用这种算法的原因显而易见。唯一值得注意的问题是我们必须确保这个图没有环。但是,如果我们知道如何建立图,那么我们或多或少会有一些附加信息来确认这个图中是否有环 – 那么此时这个线性时间内的算法就会非常适用。