了解Dijkstra算法的时间复杂度计算


71

据我了解,我使用下面给出的邻接表将Dijkstra算法的时间复杂度计算为big-O表示法。它没有按预期的方式出现,这使我逐步了解了它。

  1. 每个顶点可以连接到(V-1)个顶点,因此每个顶点的相邻边数为V-1。假设E表示连接到每个顶点的V-1边。
  2. 在最小堆中查找和更新每个相邻顶点的权重为O(log(V))+ O(1)或O(log(V))
  3. 因此,从上面的步骤1和步骤2开始,更新顶点的所有相邻顶点的时间复杂度为E *(logV)。或E*logV
  4. 因此,所有V个顶点的时间复杂度为V *(E * logV)即O(VElogV)

但是Dijkstra算法的时间复杂度为O(ElogV)。为什么?

Answers:


87

Dijkstra的最短路径算法是O(ElogV)

  • V 是顶点数
  • E 是边的总数

您的分析是正确的,但是符号有不同的含义!您说算法在O(VElogV)哪里:

  • V 是顶点数
  • E 是连接到单个节点的最大边数。

让我们将您重命名EN。所以一种分析说O(ElogV),另一种说O(VNlogV)。两者都是正确的,事实上E = O(VN)。不同之处在于这ElogV是一个更严格的估计。


5
谢谢,您的意思是,nextedgeEdges * totalVertices = totalEdges(E)。但是您能否阐明更严格的界限/估计的真正含义。
Meena Chaudhary 2014年

7
@MeenaChaudhary,更确切地说maxAdjacentEdgesOfAVertex * totalVertices >= totalEdges,这就是界限。严格的界限意味着更接近事实的估计。例如,如果算法执行n + 10运算,则可以说O(n^2)是对的,也O(nlogn)可以是对的。但这也O(n)比其他两个更紧密。可能的最紧密边界被称为Θ,因此在上面的示例中n + 1 = Θ(n)。(的定义Θ是上限和下限)
Shahbaz 2014年

3
@SeldomNeedy,是的,log以2为底。尽管只要使用O符号,它就没有区别,因为log[10](V) = log[10](2)*log[2](V),所以区别仅在于一个常数,不会改变算法的时间顺序。
Shahbaz 2015年

2
@SeldomNeedy,哦,使用计算机算法,log除2以外的任何其他基数都很少需要,因此隐含2基数。(看看我在那儿做了什么?)
Shahbaz

5
我想指出的是,此时间复杂度O(E log V)假设给定的图形已连接。例如,在稀疏图具有许多孤立顶点的情况下,它将不成立。这就是为什么Dijkstra二进制堆实现的最坏情况是O(V log V + E log V)。当我们无法假设时E >= V,它就不能简化为O(E log V)
DBedrenko

6

设n为顶点数,m为边数。

由于使用Dijkstra的算法,您有O(n)个delete-min s和O(m)reduction_key s(每个开销为O(logn)),因此使用二进制堆的总运行时间为O(log(n)(m + n)) 。这是完全可能的摊销成本decrease_key使用导致的O总运行时间斐波那契堆下降到O(1)(nlogn + M),但在实践中,这往往不是因为FHS的常数因子的处罚做了相当大的在随机图上,reduction_key s的数量远低于其各自的上限(在O(n * log(m / n)的范围内更大,在m = O(n)的稀疏图上更好)。因此,请始终注意一个事实,即总运行时间取决于您的数据结构和输入类。


4

据我了解,添加了更详细的解释,以防万一:

  • O(对于使用最小堆的每个顶点:线性地对每个边缘:将顶点推到边缘指向的最小堆)
  • V =顶点数
  • O(V * (从最小堆中弹出的顶点+在边缘中找到未访问的顶点*将其推到最小堆中))
  • E =每个顶点上的边数
  • O(V * (从最小堆弹出的顶点+ E *将未访问的顶点推送到最小堆))。请注意,在这里我们可以多次推送相同的节点,然后才能“访问”它。
  • O(V * (log(堆大小) + E * log(堆大小)))
  • O(V * ((E + 1) * log(堆大小)))
  • O(V * (E * log(堆大小)))
  • E = V 因为每个顶点都可以引用所有其他顶点
  • O(V * (V * log(堆大小)))
  • O(V^2 * log(堆大小))
  • 堆大小是V^2因为每次我们想要更新距离时都推入它,并且每个顶点最多可以有V个比较。例如,对于最后一个顶点,第一个顶点具有distance 10,第二个顶点具有9,第三个顶点具有8,依此类推,因此,我们每次推动更新
  • O(V^2 * log(V^2))
  • O(V^2 * 2 * log(V))
  • O(V^2 * log(V))
  • V^2也是边的总数,所以如果我们让它E = V^2(如官方命名那样),我们将得到O(E * log(V))

很好放!
Rishi

@sam您可以解释这部分““ E = V,因为每个顶点都可以引用所有其他顶点”
MrA

1
@MrA如果您具有顶点A,B,C,则A可以连接到B和C。B可以连接到A和C。C可以连接到A和B。因此,任何顶点都可以连接到V-1个总顶点( (本身除外),因此每个顶点上可以有V-1个边,基本上等于V。因此,E = V
山姆

2

在密集(或完整)图中, E logV > V^2

使用链接数据和二进制堆并不总是最好的。

在这种情况下,我更喜欢仅使用矩阵数据并逐行保存最小长度。

只是V^2需要时间。

以防万一E < V / logV

或者,每个顶点的最大边小于某个常数K

然后使用二进制堆。


0

让我们尝试分析CLRS本书中给出的算法。

在此处输入图片说明

对于第7行中的每个循环:对于任何顶点,请说“ u”,循环运行的次数等于“ u”的相邻顶点的数目。节点的相邻顶点数始终小于或等于图中的边总数。

如果我们取V(由于第4行中的while循环)和E(由于第7行中的每个循环)并将其复杂度计算为V E log(V),则相当于假设每个顶点上都有E边入射,但实际上在单个顶点上入射的边缘最多不超过E个。(对于单个顶点而言,至多E个相邻顶点发生在内部顶点为星图的情况下)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.