【问题标题】:Understanding Time complexity calculation for Dijkstra Algorithm了解 Dijkstra 算法的时间复杂度计算
【发布时间】:2014-12-20 06:51:09
【问题描述】:

根据我的理解,我使用下面给出的邻接表将 Dijkstra 算法的时间复杂度计算为大 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)。为什么?

【问题讨论】:

    标签: algorithm graph big-o time-complexity dijkstra


    【解决方案1】:

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

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

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

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

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

    【讨论】:

    • 谢谢,明白了,相邻边*totalVertices = totalEdges(E)。但是您能否阐明更严格的界限/估计的真正含义。
    • @MeenaChaudhary,更准确地说是maxAdjacentEdgesOfAVertex * totalVertices >= totalEdges,这就是更紧密的界限。更严格的界限意味着更接近事实的估计。例如,如果一个算法执行n + 10 操作,你可以说它是O(n^2) 是真的,或者O(nlogn) 也是真的。但它也是O(n),它比其他两个更紧密。可能的最紧密界限称为Θ,因此在上面的示例中为n + 1 = Θ(n)。 (Θ的定义既是上限又是下限)
    • @SeldomNeedy, 是的,那是log in base 2. 虽然只要O 表示法,它并没有什么区别,因为log[10](V) = log[10](2)*log[2](V),所以区别是只是一个常数,不会改变算法的时间顺序。
    • @SeldomNeedy,哦,使用计算机算法,您很少需要除 2 以外的任何基数的 log,因此隐含了基数 2。 (看看我在那里做了什么?)
    • 我想指出,这个时间复杂度,O(E log V),假设给定的图是连通的。例如,在具有很多孤立顶点的稀疏图的情况下,它不会成立。这就是为什么 Dijkstra 二进制堆实现的最坏情况是O(V log V + E log V)。当我们不能假设 E >= V 时,它不能简化为 O(E log V)
    【解决方案2】:

    添加一个更详细的解释,以防万一:

    • O(for each vertex using min heap: for each edge linearly: push vertices to min heap that edge point to )
    • V = 顶点数
    • O(V * (从最小堆中弹出顶点+在边中找到未访问的顶点*将它们推送到最小堆))
    • E = 每个顶点的边数
    • O(V * (pop 来自最小堆的顶点+ 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 次比较。例如。对于最后一个顶点,第一个顶点距离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))

    【讨论】:

    • 说得很好!!
    • @sam 你能解释一下这部分吗""E = V 因为每个顶点都可以引用所有其他顶点"
    • @MrA 如果您有顶点 A、B、C,那么 A 可以连接到 B 和 C。B 可以连接到 A 和 C。C 可以连接到 A 和 B。所以任何顶点都可以连接到总共V-1个顶点(除了自身),所以每个顶点上可以有V-1条边,基本等于V。所以,E = V
    【解决方案3】:

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

    由于使用 Dijkstra 的算法,您有 O(n) delete-mins 和 O(m) decrease_keys,每个成本 O(logn),即总运行时间使用二进制堆将是 O(log(n)(m + n))。完全有可能使用斐波那契堆将 decrease_key 的成本摊销到 O(1),从而导致总运行时间为 O(nlogn+m),但实际上这通常不会这样做,因为常数FH 的因子惩罚非常大,并且在随机图上 decrease_keys 的数量远低于其各自的上限(更多在 O(n*log(m/n) 的范围内,即在 m = O(n)) 的稀疏图上要好得多。因此请始终注意,总运行时间取决于您的数据结构和输入类。

    【讨论】:

      【解决方案4】:

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

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

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

      只需V^2 时间。

      以防万一,E < V / logV

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

      然后使用二叉堆。

      【讨论】:

        【解决方案5】:

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

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

        如果我们取 V(因为第 4 行中的 while 循环)和 E(因为第 7 行中的 for each)并将复杂度计算为 VElog(V),它将等价于假设每个顶点都有 E 条入射在其上,但实际上最多或少于 E 条入射在单个顶点上。 (单个顶点的最多 E 个相邻顶点发生在内部顶点的星图的情况下)

        【讨论】:

          【解决方案6】:

          V:顶点数, E:总边数 假设图是密集的 复杂度为 O(V*logV) + O( (1+2+...+V)*logV)

          1+2+....+(V-1) = (v)*(v+1)/2 ~ V^2 ~ E 因为图是密集的 所以复杂度是 O(ElogV)。

          和 1+2+...+ V 指的是:对于每个顶点 v 在 G.adj[u] 但不在 S 如果您在提取顶点之前考虑 Q 有 V 个顶点然后它有 V-1 然后 V-2 ...然后是 1。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-09-22
            • 2020-04-15
            • 2019-05-14
            • 2011-06-21
            • 2021-04-22
            • 2021-08-25
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多