我尝试在不使用优先级队列(Heap)的情况下在循环加权图上使用Djikstra算法并且它有效。
然后我搜索谷歌“为什么我们需要一个优先级队列来实现这个?” 搜索结果后,我浏览了维基百科,在那里我了解到原始实现不使用优先级队列,并在O(| V | 2)中运行,即V平方时间。
现在如果我们只删除优先级队列并使用正常队列,则运行时间是线性的,即O(V + E)。
请有人建议那为什么我们需要优先队列?
答案 0 :(得分:7)
我有完全相同的疑问并找到了一个测试用例,其中没有priority_queue的算法不起作用。
我们假设我有一个图表对象g
,一种方法addEdge(a,b,w)
,它将顶点a
的边添加到权重为{{1}的顶点b
}。
现在,让我定义以下图表: -
w
现在,假设我们的队列按以下顺序包含节点 Graph g
g.addEdge(0,1,5) ;
g.addEdge(1,3,1) ;
g.addEdge(0,2,2) ;
g.addEdge(2,1,1) ;
g.addEdge(2,3,7) ;
因此,首先访问节点0,然后访问节点1。
此时,使用路径{0,1,2,3 }
将dist b / w 0和3计算为6,并将1标记为已访问。
现在访问节点2并使用路径0->1->3
将dist b / w 0和1更新为值3,但由于节点1被标记为已访问,因此您无法更改距离b / w 0和3 (使用最佳路径)('0-> 2-> 1-> 3)是4.
因此,您的算法在不使用priority_queue的情况下失败。
它报告dist b / w 0和3为6,而实际上它应该是4.
现在,这是我用于实现算法的代码: -
0->2->1
正如所料,结果如下: -
使用priority_queue
0
3
2
4
现在使用无优先级队列
0
3
2
6
答案 1 :(得分:2)
对于稀疏图,如果使用二进制最小堆运行时实现(E * logV),但是如果使用Fibonacci堆实现它,则运行时将是(VlogV + E)。
答案 2 :(得分:1)
就像Moataz Elmasry所说,你可以期待的最好的是具有fib队列的O(| E | + | V |。| logV |)。至少在谈到大的价值时。
它背后的想法是,对于您当前正在处理的每个顶点(节点),您已经找到了最短的路径。如果顶点不是最小的,(距离+边缘权重)不一定是真的。这是允许您在扩展(?)从初始顶点可到达的每个顶点后立即停止算法的原因。如果您没有扩展最小的顶点,则无法保证找到最短的路径,因此您必须测试每条路径,而不仅仅是一条路径。因此,您不必经过一条路径中的每条边,而是遍历每条路径中的每条边。
您对O(E + V)的估计可能是正确的,另一方面您确定的路径和成本是不正确的。如果我没有弄错的话,如果你从每个顶点出发的第一条边缘碰巧是最小的那条,那么这条路只会是最短的。
所以Dijkstra没有优先级队列的最短路径算法只是Dijkstra的路径算法;)
答案 3 :(得分:0)
堆是此任务的最佳选择,因为它保证O(log(n))为我们的队列添加边并删除顶部元素。优先级队列的任何其他实现都会牺牲添加到我们的队列或从中删除以在其他地方获得性能提升。根据图表的稀疏程度,您可以使用不同的优先级队列实现找到更好的性能,但一般来说,最小堆最好,因为它平衡了两者。
不是最好的来源,但是:http://en.wikipedia.org/wiki/Heap_(data_structure)
答案 4 :(得分:0)
优先级队列的原因是Dijkstra的原始算法因负权重而失败。
对于任何带有加权边的有向图,可以证明Dijkstra是正确的(1)。但是,如果引入负边缘,则证明会失败,因为算法会失败。
请记住,如果图形包含负圆(所有边的总和为负的圆),具有优先级队列的Dijkstra仍可能会失败。