繁体   English   中英

当这个常规队列版本也正确时,为什么 Dijkstra 的算法需要优先级队列?

[英]Why does Dijkstra's algorithm need a priority queue when this regular queue version is also correct?

我已阅读以下内容,但请查看下面的代码。

为什么 Dijkstra 的算法使用堆(优先队列)?

我有两个版本的 dijkstra,一个带有 PQueue 的好版本,一个带有常规链表队列的坏版本。

public static void computeDijkstra(Vertex source) {
    source.minDistance = 0.;
    Queue<Vertex> vertexQueue = new PriorityQueue<Vertex>();
    // Queue<Vertex> vertexQueue = new LinkedList<Vertex>();
    vertexQueue.add(source);

    while (!vertexQueue.isEmpty()) {
        Vertex fromVertex = vertexQueue.poll();

        if (fromVertex.neighbors != null) {
            for (Edge currentEdge : fromVertex.neighbors) {
                Vertex toVertex = currentEdge.target;
                if (currentEdge.weight + fromVertex.minDistance < toVertex.minDistance) {
                    toVertex.minDistance = currentEdge.weight + fromVertex.minDistance;
                    toVertex.previous = fromVertex;
                    vertexQueue.add(toVertex);
                }
            }
        }
    }
}

public static void computeDijkstraBad(Vertex source) {
    source.minDistance = 0.;
    // Queue<Vertex> vertexQueue = new PriorityQueue<Vertex>();
    Queue<Vertex> vertexQueue = new LinkedList<Vertex>();
    vertexQueue.add(source);

    while (!vertexQueue.isEmpty()) {
        Vertex fromVertex = vertexQueue.poll();

        if (fromVertex.neighbors != null) {
            for (Edge currentEdge : fromVertex.neighbors) {
                Vertex toVertex = currentEdge.target;
                if (currentEdge.weight + fromVertex.minDistance < toVertex.minDistance) {
                    toVertex.minDistance = currentEdge.weight + fromVertex.minDistance;
                    toVertex.previous = fromVertex;
                    vertexQueue.add(toVertex);
                }
            }
        }
    }
}

我还使用如下文本文件创建图形

0, 1, 2, 3, 4, 5, 6 // vertices
0, 6 // from and to vertex
1, (2-5, 0-4, 4-6) // from vertex 1 it will have edge to 2 with weight 5 ...
0, (4-3, 3-7)
4, (2-11, 3-8)
3, (2-2, 5-5)
2, (6-2, 5-10)
5, (6-3)

两种实现都呈现以下[0, 3, 2, 6] ,这确实是从 0 到 6 的最短路径!

现在我们知道,如果使用 Simple BFS 来寻找正整数的最短距离,就会出现找不到最小路径的情况。 那么,有人可以给我一个反例,我的 Bad 实现将无法打印图表的正确路径。 随时以我使用的图形格式(示例文本文件格式)给我答案。

到目前为止,我所拥有的所有图表,两种实现都呈现了正确的结果。 这不应该发生,因为糟糕的实现是运行时 (E+V),我们知道如果没有至少 E log V,我们就找不到最短路径。

另一个例子,

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
0, 10
0, (1-9, 2-10, 3-11)
1, (4-1, 5-7)
2, (4-4, 5-3, 6-5)
3, (5-1, 6-4)
4, (7-9, 8-14, 5-3)
5, (7-4, 8-5, 9-9, 6-2)
6, (8-2, 9-2)
7, (10-3)
8, (10-2)
9, (10-5)

两种实现都渲染 [0, 3, 5, 6, 8, 10],这是从 0-10 的正确最短路径。

我相信您给出的算法是正确的,但它不如 Dijkstra 的算法有效。

在高层次上,您的算法通过找到一个“活动”节点(距离已降低的节点),然后扫描出边以激活所有需要更新其距离的相邻节点来工作。 请注意,同一个节点可以被多次激活——事实上,一个节点可能会在每次其候选距离下降时被激活一次,这在算法的运行中可能会发生多次。 此外,如果候选距离多次下降,您实现的算法可能会将同一个节点多次放入队列,因此除了第一个之外的所有出队都可能是不必要的。 总体而言,我预计这会导致大图的运行时受到很大影响。

从某种意义上说,您实现的算法是最短路径算法,但它不是 Dijkstra 算法。 主要区别在于,Dijkstra 的算法使用优先级队列来确保每个节点出队和处理一次且恰好一次,从而提高了效率。

所以我想我能给出的最好答案是“你的算法不是 Dijkstra 算法的实现,Dijkstra 算法使用优先级队列的原因是为了避免像你的算法那样多次重新计算距离。”

你的算法会找到正确的结果,但你的方法正在做的是它扼杀了 Dijkstra 方法的效率。

示例:

考虑 3 个名为 AB C 的节点。

A->C :7 
A->B :2
B->C :3

在您的坏方法中,您首先将 A 到 C 的最短路径设置为 7,然后在遍历时将其修改为 5 (A->BC)

在 Dijkstra 的方法中,根本不会遍历 A->C,因为在使用最小堆时,将首先遍历 A->B,将 B 标记为“已遍历”,然后将遍历 B->C ,并且,C 将被标记为“已遍历”。 现在,由于 C 已被标记为“已遍历”,因此永远不会检查路径 A->C(长度为 7)。

因此,如您所见,在您的糟糕方法中,您将到达 C 2 次(A->C & A->B->C),而使用 Dijkstra 的方法时,您将只到达 C 一次。

这个例子应该证明你使用 Dijkstra 算法的迭代次数会更少。

看了别人的答案,他们是对的,总结是不好的实现方法是正确的,但是楼主的说法复杂度(O(E+V))不对。 其他人没有提到的另一件事,我会在这里添加。

这种糟糕的实现也对应于一种算法,它实际上是 BFS 的衍生物,正式名称为SPFA 查看SPFA算法 当作者早在 1994 年发表这个算法时,他声称它比具有 O(E) 复杂度的 Dijkstra 具有更好的性能,这是错误的。

它在 ACM 学生中非常受欢迎。 由于其简单且易于实施。 至于性能,Dijkstra 是首选。

类似的回复参考这个帖子

所有的答案都写得很好,所以我不会添加太多解释。 我正在添加一个示例,这是我在 Striver 在他的一个图表播放列表视频中发表的评论中遇到的。 希望这将有助于清楚地看到这个问题。

如果我们在为具有任意边权重的无向图实现 Dijkstra 算法时不使用优先队列,那么我们可能不得不一次又一次地访问同一个节点,它将无法考虑任意边权重。

以下面的示例为例,通过优先队列和简单队列进行空运行,您将看到您必须访问node 6两次才能进行正常队列空运行。 因此,在更大的图表的情况下,这将导致性能非常差。

编辑——5 到 6 个节点的边权重是 1 而不是 10。我会尽快上传正确的图像。

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM