关于运行时复杂性,以下内容和原因之间有什么区别?:
(1)使用常规优先级队列(堆)的DIJKSTRA算法
(2)使用双向链表的DIJKSTRA算法
(除非没有区别)
答案 0 :(得分:1)
Dijkstra算法的最通用版本假设您可以访问某种支持以下操作的优先级队列结构:
Dijkstra的算法需要一次调用make-heap,O(n)调用dequeue-min,O(m)调用reduce-key,其中n是节点数和m是边数。整个运行时间实际上可以给出为O(T mh + nT deq + mT dk ),其中T mh ,T deq 和T dk 分别是执行make-heap,dequeue和reduce-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-key的成本现在为O(1),因为您可以就地更新节点的密钥。这意味着运行时为O(n + n 2 + m)= O(n 2 + m)= O(n 2 ) ,因为边数不超过O(n 2 )。这是以前的改进。
对于二进制堆,如果使用标准线性时间堆化算法,则执行生成堆的成本为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"算法"是多么有趣?因为数据结构的选择对于快速运行的算法至关重要,所以算法的算法比单个算法更像系列。