【问题标题】:Time complexity of Prim's MST AlgorithmPrim 的 MST 算法的时间复杂度
【发布时间】:2012-10-19 09:53:23
【问题描述】:

有人可以向我解释为什么使用相邻矩阵的 Prim 算法会导致时间复杂度为 O(V<sup>2</sup>) 吗?

【问题讨论】:

标签: algorithm graph time-complexity minimum-spanning-tree prims-algorithm


【解决方案1】:

(抱歉,ASCII 数学看起来很草率,我认为我们不能使用 LaTEX 来排版答案)

实现具有O(V^2) 复杂度的 Prim 算法的传统方法是除了邻接矩阵之外还有一个数组,我们称之为distance,它具有该顶点到节点的最小距离。

这样,我们只检查distance 以找到下一个目标,并且由于我们这样做V 次并且distance 有V 个成员,我们的复杂度是O(V^2)

这本身是不够的,因为distance 中的原始值很快就会过时。为了更新这个数组,我们所做的只是在每一步结束时,遍历我们的邻接矩阵并适当地更新distance。这不会影响我们的时间复杂度,因为它仅仅意味着每一步都需要O(V+V) = O(2V) = O(V)。因此我们的算法是O(V^2)

如果不使用distance,我们必须每次都遍历所有 E 边,最坏的情况是包含 V^2 边,这意味着我们的时间复杂度将是 O(V^3)

证明:

为了证明没有distance 数组,就不可能在O(V^2) 时间内计算MST,请考虑在每次迭代中使用大小为n 的树时,可能会添加V-n 顶点.

要计算选择哪一个,我们必须检查每一个以找到它们与树的最小距离,然后将其相互比较并找到其中的最小值。

在最坏的情况下,每个节点都包含到树中每个节点的连接,导致 n * (V-n) 条边和O(n(V-n)) 的复杂度。

由于我们的总数将是 n 从 1 到 V 时每个步骤的总和,因此我们的最终时间复杂度是:

(sum O(n(V-n)) as n = 1 to V) =  O(1/6(V-1) V (V+1)) = O(V^3)

QED

【讨论】:

  • > The traditional way to implement Prim's algorithm with O(V^2) complexity is to have an array in addition to the adjacency matrix, lets call it distance which has the minimum distance of that vertex to the node. 你能详细说明数组是什么,你在里面放了什么吗?还有:lets call it,什么是“它”——数组或邻接矩阵?
  • 忽略我上面的评论,下面有人写了更好的解释
【解决方案2】:

首先,显然至少是O(V^2),因为这就是邻接矩阵的大小。

查看http://en.wikipedia.org/wiki/Prim%27s_algorithm,需要执行步骤“Repeat until Vnew = V”V次。

在该步骤中,您需要计算 V 中的任何顶点与 V 之外的任何顶点之间的最短链接。维护一个大小为 V 的数组,为每个顶点保存无穷大(如果顶点在 V 中)或长度V 中的任何顶点与该顶点之间的最短链接及其长度(所以一开始这只是来自起始顶点和所有其他顶点之间的链接长度)。要找到下一个要添加到 V 的顶点,只需搜索这个数组,成本为 V。一旦你有了一个新顶点,查看从该顶点到每个其他顶点的所有链接,看看它们中是否有从 V 到的较短链接那个顶点。如果有,请更新数组。这也花费了 V。

所以你有 V 个步骤(要添加 V 个顶点),每个步骤花费 V,这给你 O(V^2)

【讨论】:

    【解决方案3】:

    注意:这个答案只是借用jozefg's answer并试图更全面地解释它,因为我必须在理解之前思考一下。

    背景

    图的邻接矩阵表示构造了一个 V x V 矩阵(其中 V 是顶点数)。单元格(a, b)的值是连接顶点a和b的边的权重,如果没有边则为零。

    Adjacency Matrix
    
       A B C D E
    --------------
    A  0 1 0 3 2
    B  1 0 0 0 2
    C  0 0 0 4 3
    D  3 0 4 0 1
    E  2 2 3 1 0
    

    Prim's Algorithm 是一种算法,它采用一个图和一个起始节点,并在图上找到一个最小生成树 - 即它找到边的子集,因此结果是包含所有节点和组合边权重的树被最小化。可以总结如下:

    1. 将起始节点放在树中。
    2. 重复直到所有节点都在树中:
      1. 查找将树中的节点连接到树中的节点的所有边。
      2. 在这些边中,选择一个重量最小的边。
      3. 将该边和连接的节点添加到树中。

    分析

    我们现在可以像这样开始分析算法了:

    1. 在循环的每次迭代中,我们将一个节点添加到树中。由于有 V 个节点,因此该循环有 O(V) 次迭代。
    2. 在循环的每次迭代中,我们需要在树中找到并测试边。如果有 E 条边,朴素搜索实现使用 O(E) 来找到权重最小的边。
    3. 因此,综合起来,我们应该预计复杂度为 O(VE),在最坏的情况下可能为 O(V^3)。

    然而,jozefg 给出了一个很好的答案来展示如何实现 O(V^2) 复杂度。

    Distance to Tree
    
                | A  B  C  D  E
                |----------------
    Iteration 0 | 0  1* #  3  2
              1 | 0  0  #  3  2*
              2 | 0  0  4  1* 0
              3 | 0  0  3* 0  0
              4 | 0  0  0  0  0
    
    NB. # = infinity (not connected to tree)
        * = minimum weight edge in this iteration
    

    这里的距离向量表示连接每个节点到树的最小加权边,用法如下:

    1. 使用复杂度为 O(V) 的起始节点 A 的边缘权重进行初始化。
    2. 要查找下一个要添加的节点,只需找到 distance 的最小元素(并将其从列表中删除)。这是 O(V)。
    3. 添加新节点后,有 O(V) 条新边将树连接到其余节点;对于这些中的每一个,确定新边的权重是否小于现有距离。如果是,更新 distance 向量。再次,O(V)。

    使用这三个步骤可以将搜索时间从 O(E) 减少到 O(V),并添加一个额外的 O(V) 步骤来在每次迭代时更新 distance 向量。由于现在每次迭代都是 O(V),所以总体复杂度是 O(V^2)。

    【讨论】:

    • 这个答案太棒了!这应该是正确的答案!
    猜你喜欢
    • 2010-10-17
    • 1970-01-01
    • 2012-06-02
    • 2017-12-04
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    相关资源
    最近更新 更多