简体   繁体   English

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

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

I have read the following but please take a look at my code below.我已阅读以下内容,但请查看下面的代码。

Why Dijkstra's Algorithm uses heap (priority queue)? 为什么 Dijkstra 的算法使用堆(优先队列)?

I have two versions of dijkstra, one good version with PQueue, and one bad version with regular linked list queue.我有两个版本的 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);
                }
            }
        }
    }
}

I also have graph creation with a text file like below我还使用如下文本文件创建图形

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)

Both the implementation renders the following [0, 3, 2, 6] which is the shortest path indeed from 0 to 6!两种实现都呈现以下[0, 3, 2, 6] ,这确实是从 0 到 6 的最短路径!

Now we know, if a Simple BFS is used to find shortest distance with positive integers, there will be cases where it will not find the minimum path.现在我们知道,如果使用 Simple BFS 来寻找正整数的最短距离,就会出现找不到最小路径的情况。 So, can someone give me a counter example for which my Bad implementation will fail to print the right path for a graph.那么,有人可以给我一个反例,我的 Bad 实现将无法打印图表的正确路径。 Feel free to give me the answer in the graph format (the sample text file format) that I used.随时以我使用的图形格式(示例文本文件格式)给我答案。

So far all the graphs I have had, both implementations rendered the right result.到目前为止,我所拥有的所有图表,两种实现都呈现了正确的结果。 This shouldn't happen because the bad implementation is runtime (E+V) and we know we can't find shortest path without at least E log V.这不应该发生,因为糟糕的实现是运行时 (E+V),我们知道如果没有至少 E log V,我们就找不到最短路径。

Another example,另一个例子,

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)

Both implementations renders [0, 3, 5, 6, 8, 10], which is the correct shortest path from 0-10.两种实现都渲染 [0, 3, 5, 6, 8, 10],这是从 0-10 的正确最短路径。

I believe that the algorithm you've given is correct, but that it isn't as efficient as Dijkstra's algorithm.我相信您给出的算法是正确的,但它不如 Dijkstra 的算法有效。

At a high level, your algorithm works by finding an "active" node (one whose distance has been lowered), then scanning the outgoing edges to activate all adjacent nodes that need their distance updated.在高层次上,您的算法通过找到一个“活动”节点(距离已降低的节点),然后扫描出边以激活所有需要更新其距离的相邻节点来工作。 Notice that the same node can be made active multiple times - in fact, it's possible that a node will be activated once per time its candidate distance drops, which can happen potentially many times in a run of the algorithm.请注意,同一个节点可以被多次激活——事实上,一个节点可能会在每次其候选距离下降时被激活一次,这在算法的运行中可能会发生多次。 Additionally, the algorithm you have implemented might put the same node into the queue multiple times if the candidate distance drops multiple times, so it's possible that all dequeues except the first will be unnecessary.此外,如果候选距离多次下降,您实现的算法可能会将同一个节点多次放入队列,因此除了第一个之外的所有出队都可能是不必要的。 Overall, I'd expect this would result in a pretty big runtime hit for large graphs.总体而言,我预计这会导致大图的运行时受到很大影响。

In a sense, the algorithm you've implemented is a shortest paths algorithm, but it's not Dijkstra's algorithm.从某种意义上说,您实现的算法是最短路径算法,但它不是 Dijkstra 算法。 The main difference is that Dijkstra's algorithm uses a priority queue to ensure that every node is dequeued and processed once and exactly once, leading to much higher efficiency.主要区别在于,Dijkstra 的算法使用优先级队列来确保每个节点出队和处理一次且恰好一次,从而提高了效率。

So I guess the best answer I can give is "your algorithm isn't an implementation of Dijkstra's algorithm, and the reason Dijkstra's algorithm uses a priority queue is to avoid recomputing distances multiple times in the way that your algorithm does."所以我想我能给出的最好答案是“你的算法不是 Dijkstra 算法的实现,Dijkstra 算法使用优先级队列的原因是为了避免像你的算法那样多次重新计算距离。”

Your algorithm will find the right result but what your approach is doing is that it kills off the efficiency of Dijkstra's approach.你的算法会找到正确的结果,但你的方法正在做的是它扼杀了 Dijkstra 方法的效率。

Example:示例:

Consider 3 nodes named AB C.考虑 3 个名为 AB C 的节点。

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

In your bad approach, You'll first set the shortest path from A to C as 7, and then, as you traverse, you will revise it to 5 (A->BC)在您的坏方法中,您首先将 A 到 C 的最短路径设置为 7,然后在遍历时将其修改为 5 (A->BC)

In Dijkstra's approach, A->C will not be traversed at all because, when using a min-heap, A->B will be traversed first, B will be marked as "traversed", and then B->C will be traversed, and, C will be marked as "traversed".在 Dijkstra 的方法中,根本不会遍历 A->C,因为在使用最小堆时,将首先遍历 A->B,将 B 标记为“已遍历”,然后将遍历 B->C ,并且,C 将被标记为“已遍历”。 Now, since C is already marked as "traversed", the path A->C (of length 7) will never be checked.现在,由于 C 已被标记为“已遍历”,因此永远不会检查路径 A->C(长度为 7)。

Therefore, as you can see, in your bad approach, you will be reaching C 2 times (A->C & A->B->C), while using Dijkstra's approach, you will go to C only once.因此,如您所见,在您的糟糕方法中,您将到达 C 2 次(A->C & A->B->C),而使用 Dijkstra 的方法时,您将只到达 C 一次。

This example should prove that you will have fewer iterations with Dijkstra's algorithm.这个例子应该证明你使用 Dijkstra 算法的迭代次数会更少。

Read the others' answers, they are right, and the summary is the bad implement method is correct, but the complexity of the owner's claim(O(E+V)) is not Right.看了别人的答案,他们是对的,总结是不好的实现方法是正确的,但是楼主的说法复杂度(O(E+V))不对。 One other thing that no one else has mentioned, and I'll add it here.其他人没有提到的另一件事,我会在这里添加。

This poor implementation also corresponds to an algorithm, which is actually a derivative of BFS, formally known as SPFA .这种糟糕的实现也对应于一种算法,它实际上是 BFS 的衍生物,正式名称为SPFA Check out the SPFA algorithm .查看SPFA算法 When the author published this algorithm back in 1994, he claimed it has a better performance than Dijkstra with O(E) complexity, which is Wrong.当作者早在 1994 年发表这个算法时,他声称它比具有 O(E) 复杂度的 Dijkstra 具有更好的性能,这是错误的。

It became very popular among ACM students.它在 ACM 学生中非常受欢迎。 Due to its simplicity and ease to implement.由于其简单且易于实施。 As for Performance, Dijkstra is preferred.至于性能,Dijkstra 是首选。

similar reply ref to this post类似的回复参考这个帖子

All of the answers are very well written, so I won't be adding much explanation.所有的答案都写得很好,所以我不会添加太多解释。 I am adding an example that I came across in a comment made by Striver in one of his videos of the graph playlist.我正在添加一个示例,这是我在 Striver 在他的一个图表播放列表视频中发表的评论中遇到的。 Hope it will help to see the issue clearly.希望这将有助于清楚地看到这个问题。

If we don't use Priority Queue while implementing Dijkstra's Algo for an undirected graph with arbitrary edge weights then we might have to visit the same node, again and again, it will not be able to account for arbitrary edge weights.如果我们在为具有任意边权重的无向图实现 Dijkstra 算法时不使用优先队列,那么我们可能不得不一次又一次地访问同一个节点,它将无法考虑任意边权重。

Take the below example and dry run it by taking a Priority queue and by taking a simple queue, you will see that you have to visit node 6 twice for the normal queue dry run.以下面的示例为例,通过优先队列和简单队列进行空运行,您将看到您必须访问node 6两次才能进行正常队列空运行。 So, in the case of much larger graphs, this will result in very poor performance.因此,在更大的图表的情况下,这将导致性能非常差。

Edit-- 5 to 6 node edge weight would be 1 and not 10. I will upload the correct image soon.编辑——5 到 6 个节点的边权重是 1 而不是 10。我会尽快上传正确的图像。

在此处输入图像描述

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

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