使用最小优先级队列时,如何跟踪 Dijkstra 算法中的最短路径?

Posted

技术标签:

【中文标题】使用最小优先级队列时,如何跟踪 Dijkstra 算法中的最短路径?【英文标题】:How do I keep track of the shortest paths in the Dijkstra algorithm when using a min-priority queue? 【发布时间】:2019-10-29 17:49:18 【问题描述】:

我正在尝试使用优先级队列实现 Dijkstra 算法。

据我了解,“Dijkstra 算法”允许找到最短的“路径”,因为它将返回一组构成最短路径的顶点 *。

从这里的https://***.com/a/20217659/1663462 和 (Dijkstra's_algorithm#Algorithm) 的答案看来,我应该能够只使用 两个 数据结构来实现它:图形和队列数据结构。


但是,在我使用上述两个数据结构的实现中,当我最终到达目标节点时,我没有存储顶点路径吗?换句话说,我只有最短的distance(单个标量值)。

这意味着如何跟踪?我能想到的唯一方法是使用附加数据结构 - 一个数组或哈希映射,其中key 是顶点,value 是它的父节点。


实际问题:

是否需要额外的数据结构来实现(“形成最短路径的顶点集 *”)?如果不是,我如何确定顶点?

【问题讨论】:

在我的脑海中,因为我在遍历 Neo4j 图形时做了同样的事情,是的,你需要额外的映射级别。 IIRC 您根据距离添加节点,因此每个距离都需要一个队列,然后始终从与最短距离关联的队列中删除。听起来你的想法倒退了。第一个键基于长度(距离)。这将是整数到队列的映射。然后在队列中是顶点(节点)。由于它们都具有相同的距离(成本因素),我想不出让它们分类的理由。 LIFO 或 FIFO 有效。 只是一个想法(尚未全部阅读)。如果您的顶点是对象,您还可以在每个节点中添加一个前驱(您的节点通过最短路径到达它)。这样,一旦到达目标,您就可以简单地使用此实例变量向后遍历。 @jottbe 与直接修改节点相比,将前辈存储在单独的表中可能会更好。除了挑剔你是对的。 @Paul:好的,我认为你是对的。我只是假设“节点”只是为 dijkstra 算法创建的。 所以这意味着 Wikipedia 文章中的算法不准确? 【参考方案1】:

您不必按照您的建议跟踪每个顶点的整个路径。要自己生成s-v 路径,您必须为每个顶点记录v 的唯一内容就是“发现”它的边。

换句话说,当算法发现顶点v时,您记录边(u,v),它在该边上实现了与s的距离最小化的值。

现在,假设图中每个顶点v 都有“发现”边,从sv 的路径可以计算如下:如果(u,v) 是(“发现”)为v存储的边,那么从sv的最短路径是从su的路径(可以递归计算),然后是单边(u,v)

因此,要构造从sv 的最短路径,您从顶点v 开始,然后沿相反方向沿着为v 存储的边,并继续直到到达s .

【讨论】:

【参考方案2】:

是的,额外的数据结构是必要的,我没有找到没有它的方法。

没有它的两个顶点之间的“距离”最短,但它不包括源顶点和目标顶点之间的顶点列表。

【讨论】:

这不是真的,不需要额外的结构。我发布了一个解释原因的答案。【参考方案3】:

虽然在运行 Dijkstra 算法时可以在节点上存储额外的信息,但这并不是绝对必要的。

确实,在运行 Dijkstra 算法后,可以追溯一条最短路径,即使节点上没有存储额外信息。每个节点所需的唯一信息是它与源的距离。

我将使用以下符号:

s 是源顶点; t 是目标顶点; w(u,v) 是两个顶点uv 之间的边(u,v) 的长度(随图给出); d(v) 是从源 s 到顶点 v 的最短路径的长度(由 Dijkstra 计算)。

如果 Dijkstra 的算法运行正确,那么以下命题对于除源之外的所有顶点v 都必须为真:

顶点v 至少有一个邻居u 使得d(v) = d(u) + w(u,v)

对于每个顶点v,调用该邻居的pred(v)

然后可以反向构造最短路径,从目标t 开始,沿着pred 顶点。

【讨论】:

以上是关于使用最小优先级队列时,如何跟踪 Dijkstra 算法中的最短路径?的主要内容,如果未能解决你的问题,请参考以下文章

修改队列内容后如何从优先级队列中获取最小元素[重复]

Dijkstra+优先队列

Dijkstra 算法运行时的区别:优先队列与双向链表

最短路Dijkstra+ 链式前向星+ 堆优化(优先队列)

POJ 1511 Invitation Cards(dijkstra+优先队列)

D. Buy a Ticket(优先队列+dijkstra)