我将基于networkx的实现。
双向 Dijkstra 在两个方向遇到相同节点时会停止 - 但它在该点返回的路径可能不是通过该节点。它正在执行额外的计算来跟踪最短路径的最佳候选者。
我的解释将基于您的评论(this answer)
考虑这个简单的图(带有节点 A、B、C、D、E)。该图的边及其权重为:"A->B:1","A->C:6","A->D:4","A->E:10","D-> C:3","C->E:1"。当我在两边对该图使用 Dijkstra 算法时:向前它在 A 和 D 之后找到 B,在向后它在 E 和 D 之后找到 C。在这一点上,两个集合都有相同的顶点和一个交点。这是终止点还是必须继续?因为这个答案(A->D->C->E)是不正确的。
供参考,这是图表:
当我在反例中的(无向)网络上运行 networkx 的双向 dijkstra 时,您声称该评论:"A->B:1","A->C:6","A->D:4","A->E:10","D->C:3","C->E:1":它给了我:(7, ['A', 'C', 'E']),而不是 A-D-C-E。
问题在于对它在停止之前在做什么的误解。它在查找节点方面完全符合您的期望,但是在执行此操作时,会发生额外的处理来查找最短路径。当它从两个方向到达D 时,它已经收集了一些其他可能更短的“候选”路径。不能保证仅仅因为节点D 从两个方向到达,最终成为最短路径的一部分。相反,在从两个方向都到达一个节点时,当前候选最短路径比它继续运行时找到的任何候选路径都要短。
算法从两个空簇开始,每个簇与A或E相关联
{} {}
它会在每个周围建立“集群”。它首先将A 放入与A 关联的集群中
{A:0} {}
现在它检查A 是否已经在E 周围的集群中(当前为空)。它不是。接下来,它查看A 的每个邻居,并检查它们是否在E 周围的集群中。他们不是。然后它将所有这些邻居放入A 的即将到来的邻居的堆(如有序列表)中,按来自A 的路径长度排序。将此称为A 的“边缘”
clusters ..... fringes
{A:0} {} ..... A:[(B,1), (D,4), (C,6), (E,10)]
E:[]
现在它检查E。对于E,它会做对称的事情。将E 放入其集群中。检查E 是否不在A 周围的集群中。然后检查它的所有邻居,看看A 周围的集群中是否有任何邻居(它们不是)。然后创建E 的边缘。
clusters fringes
{A:0} {E:0} ..... A:[(B,1), (D,4), (C,6), (E,10)]
E:[(C,1), (A,10)]
现在它回到A。它从列表中获取B 并将其添加到A 周围的集群中。它检查B 的任何邻居是否在E 周围的集群中(没有要考虑的邻居)。所以我们有:
clusters fringes
{A:0, B:1} {E:0} ..... A:[(D,4), (C,6), (E,10)]
E:[(C,1), (A,10)]
回到E:我们将C添加到E的集群中,并检查C的任何邻居是否在A的集群中。你知道吗,有A。所以我们有一个候选最短路径 A-C-E,距离为 7。我们会坚持下去。我们将D 添加到E 的边缘(距离为4,因为它是1+3)。我们有:
clusters fringes
{A:0, B:1} {E:0, C:1} ..... A:[(D,4), (C,6), (E,10)]
E:[(D,4), (A,10)]
candidate path: A-C-E, length 7
回到A:我们从它的边缘得到下一个东西,D。我们将它添加到大约A 的集群中,并注意它的邻居C 在大约E 的集群中。所以我们有一个新的候选路径,A-D-C-E,但它的长度大于 7,所以我们丢弃它。
clusters fringes
{A:0, B:1, D:4} {E:0, C:1} ..... A:[(C,6), (E,10)]
E:[(D,4), (A,10)]
candidate path: A-C-E, length 7
现在我们回到E。我们看D。它位于A 周围的集群中。我们可以肯定,我们将遇到的任何未来候选路径的长度至少与我们刚刚追踪的A-D-C-E 路径一样大(这种说法不一定很明显,但它是这种方法的关键)。所以我们可以停下来。我们返回之前找到的候选路径。