Dijkstra算法的运行时间差异:优先级队列与双向链接列表

关于运行时复杂性,以下内容和原因之间有什么区别?:

(1)使用常规优先级队列(堆)的DIJKSTRA算法

(2)DIJKSTRA的算法使用双向链表

(除非没有区别)

最佳答案 Dijkstra算法的最通用版本假定您可以访问某种支持以下操作的优先级队列结构:

> make-heap(s,n):在初始距离为∞的情况下构建n个节点的堆,除了起始节点s,其距离为0.
> dequeue-min():删除并返回优先级最低的元素.
> decrease-key(obj,key):给定优先级队列中的现有对象obj,将其优先级降低到key给出的级别.

Dijkstra的算法需要一次调用make-heap,O(n)调用dequeue-min,O(m)调用reduce-key,其中n是节点数,m是边数.整个运行时间实际上可以给出为O(Tm-h nTdeq mTd-k),其中Tm-h,Tdeq和Td-k是进行堆积,出列和减少的平均(摊销)成本-key,分别.

现在,让我们假设您的优先级队列是双向链表.实际上有几种方法可以将双向链表用作优先级队列:您可以按节点对节点进行排序,也可以将它们保持为未排序顺序.让我们考虑其中的每一个.

在排序的双向链表中,执行生成堆的成本是O(n):只需插入起始节点,然后在距离无穷大处插入n – 1个其他节点.执行dequeue-min的成本是O(1):只删除第一个元素.但是,执行减少键的成本是O(n),因为如果您需要更改节点的优先级,您可能必须移动它,并且您无法找到移动它的位置(在最坏的情况下)在节点上进行线性扫描.这意味着运行时将为O(n n nm)= O(mn).

在未排序的双向链表中,执行make-heap的成本仍为O(n),因为您需要创建n个不同的节点. dequeue-min的成本现在为O(n),因为您必须对列表中的所有节点进行线性扫描才能找到最小值.但是,reduce键的成本现在为O(1),因为您可以就地更新节点的键.这意味着运行时间为O(n n2 m)= O(n2 m)= O(n2),因为边数不会超过O(n2).这是以前的改进.

对于二进制堆,如果使用标准线性时间堆化算法,则执行生成堆的成本为O(n).进行出队的成本是O(log n),并且做一个减少键的成本也是O(log n)(只是将元素向上冒泡直到它在正确的位置).这意味着具有二进制堆的Dijkstra算法的运行时间为O(n n log n m log n)= O(m log n),因为如果图形连接,我们将m≥n.

你可以用渐近的意义上的Fibonacci堆做得更好.它是专门用于快速制作Dijkstra算法的专用优先级队列.它可以在时间O(n)中执行生成堆,在时间O(log n)中执行dequeue-min,并在(摊销)O(1)时间执行减少键.这使得Dijkstra算法的运行时间O(n n log n m)= O(m n log n),但实际上常数因子使得Fibonacci堆比二进制堆慢.

所以你有它!不同的优先级队列确实有所作为.有趣的是,“Dijkstra算法”更像是一类算法,而不是单一算法,因为数据结构的选择对于快速运行算法至关重要.

点赞