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

时间:2016-04-28 16:48:33

标签: algorithm graph dijkstra

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

Why Dijkstra's Algorithm uses heap (priority queue)?

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

document.addEventListener('click', function xyz(e){
e.preventDefault();
//alert(e);
var target = e.target || event.srcElement;
var attributes = Array.prototype.slice.call(target.attributes).map(function(i)    {
    return [String(i.name)+": "+String(i.value)]
})
alert(attributes);
prompt("xpath1 :",getPathTo(target));
chrome.runtime.sendMessage({method:"captureElement",data:attributes});   
},true)

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

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到6的最短路径!

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

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

另一个例子,

[0, 3, 2, 6]

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

3 个答案:

答案 0 :(得分:3)

我相信你给出的算法是正确的,但它没有Dijkstra算法那么有效。

在较高级别,您的算法通过查找“活动”节点(距离已降低的节点),然后扫描输出边缘以激活需要更新距离的所有相邻节点来工作。请注意,同一节点可以多次激活 - 事实上,每次候选距离下降时,节点可能会被激活一次,这可能会在算法运行中多次发生。此外,如果候选距离下降多次,您实施的算法可能会多次将同一节点放入队列,因此除了第一个之外的所有队列都可能是不必要的。总的来说,我认为这会导致大型图形的运行时间大幅增加。

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

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

答案 1 :(得分:2)

您的算法将找到正确的结果,但您的方法正在做的是它会降低Dijkstra方法的效率。

示例:

考虑名为A B C的3个节点。

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

在你糟糕的方法中,你首先将从A到C的最短路径设置为7,然后,当你遍历时,你将它修改为5(A-> B-C)

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

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

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

答案 2 :(得分:0)

阅读其他人的答案,他们是正确的,并且总结是错误的实施方法是正确的,但是所有者主张的复杂度(O(E + V))是不正确的。没人提及的另一件事,我将在这里添加。

这种糟糕的实现方式也对应于一种算法,该算法实际上是BFS的派生形式,正式名称为 SPFA 。签出SPFA algorithm。当作者于1994年发布此算法时,他声称它的性能优于Dijkstra(具有O(E)复杂度),这是错误的。

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

this post的类似回复