Dijkstra的算法教给我如下
while pqueue is not empty:
distance, node = pqueue.delete_min()
if node has been visited:
continue
else:
mark node as visited
if node == target:
break
for each neighbor of node:
pqueue.insert(distance + distance_to_neighbor, neighbor)
但是我一直在阅读有关该算法的一些阅读,我看到很多版本都使用了reduce-key而不是insert。
为什么会这样,这两种方法之间有什么区别?
答案 0 :(得分:57)
使用reduce-key而不是重新插入节点的原因是为了使优先级队列中的节点数保持较小,从而保持优先级队列的总数较少,并且每个优先级队列的成本平衡较低。
在Dijkstra算法的实现中,该算法将节点重新插入具有新优先级的优先级队列,将一个节点添加到图中每个m个边缘的优先级队列中。这意味着在优先级队列上有m个入队操作和m个出列操作,总运行时间为O(m T e + m T d ),其中T < sub> e 是排入优先级队列所需的时间,T d 是从优先级队列中出队所需的时间。
在支持reduce-key的Dijkstra算法的实现中,保存节点的优先级队列以其中的n个节点开始,并且在算法的每个步骤中移除一个节点。这意味着堆出队的总数是n。对于通向它的每个边缘,每个节点都会有一个为其调用的减少键,因此减少键的总数最多为m。这给出了运行时间(n T e + n T d + m T k ),其中T k 是调用减小键所需的时间。
那么这对运行时有什么影响?这取决于您使用的优先级队列。这是一个快速表,显示了不同优先级队列和不同Dijkstra算法实现的总体运行时间:
Queue | T_e | T_d | T_k | w/o Dec-Key | w/Dec-Key
---------------+--------+--------+--------+-------------+---------------
Binary Heap |O(log N)|O(log N)|O(log N)| O(M log N) | O(M log N)
Binomial Heap |O(log N)|O(log N)|O(log N)| O(M log N) | O(M log N)
Fibonacci Heap | O(1) |O(log N)| O(1) | O(M log N) | O(M + N log N)
正如您所看到的,对于大多数类型的优先级队列,渐近运行时确实没有区别,而减少键的版本不太可能做得更好。但是,如果您使用优先级队列的Fibonacci heap实现,那么在使用reduce-key时,Dijkstra的算法确实会渐进地提高效率。
简而言之,使用减少键和优先级优先级队列,可以使Dijkstra的渐近运行时间超出可能的范围,如果你继续排队和出列队列。
除此之外,一些更先进的算法,如Gabow的最短路径算法,使用Dijkstra算法作为子程序,并严重依赖于减少键实现。他们使用的事实是,如果您事先知道有效距离的范围,您可以根据这一事实构建一个超高效的优先级队列。
希望这有帮助!
答案 1 :(得分:22)
2007年,有一篇论文研究了使用reduce-key版本和插入版本之间执行时间的差异。见http://www.cs.utexas.edu/users/shaikat/papers/TR-07-54.pdf
他们的基本结论是不对大多数图表使用减少键。特别是对于稀疏图,非减少键明显快于减少键版本。有关详细信息,请参阅该文章。
答案 2 :(得分:1)
有两种实现Dijkstra的方法:一种使用支持减少键的堆,另一种使用不支持减少键的堆。
它们通常都有效,但通常是后者。 在下文中,我将使用'm'表示边的数量,使用'n'表示图形的顶点的数量:
如果您想要最大可能的最坏情况下的复杂性,则可以使用支持减少键的Fibonacci堆:您将获得一个不错的O(m + nlogn)。
如果您关心平均情况,那么也可以使用Binary堆:您将获得O(m + nlog(m / n)logn)。证明为here,第99/100页。如果图是密集的(m >> n),则此图和前一个图都趋于O(m)。
如果您想知道在实图上运行它们会发生什么情况,可以检查一下this纸张,就像马克·梅克顿(Mark Meketon)在他的答案中建议的那样。
实验结果将表明,在大多数情况下,“简单”堆将提供最佳结果。
实际上,在使用减少键的实现中,Dijkstra在使用简单的Binary堆或Pairing堆时比使用Fibonacci堆时表现更好。这是因为斐波那契堆涉及更大的常数因子,并且减少键操作的实际数量往往比最坏情况所预测的要少得多。
出于类似的原因,不必支持减键操作的堆,其常量因子甚至更少,并且实际上性能最佳。尤其是在图形稀疏的情况下。