Java 使用优先级队列的Prims算法的复杂性?

Java 使用优先级队列的Prims算法的复杂性?,java,algorithm,minimum-spanning-tree,prims-algorithm,Java,Algorithm,Minimum Spanning Tree,Prims Algorithm,我用的是邻接矩阵,优先级是队列的数据结构 根据我的计算,复杂性是V^3 log V: While循环:V 检查相邻顶点:V 如果条目已经存在,则检查队列,并更新该条目:V log V 但是,我到处都读到复杂性是V^2 请解释一下 如果使用斐波那契堆,则提取最小值为O(lg V)摊余成本,并更新其中的条目为O(1)摊余成本 如果我们使用这个伪代码 while priorityQueue not empty u = priorityQueue.exractMin() for ea

我用的是邻接矩阵,优先级是队列的数据结构

根据我的计算,复杂性是
V^3 log V

  • While循环:
    V
  • 检查相邻顶点:
    V
  • 如果条目已经存在,则检查队列,并更新该条目:
    V log V
但是,我到处都读到复杂性是
V^2


请解释一下

如果使用斐波那契堆,则提取最小值为
O(lg V)
摊余成本,并更新其中的条目为
O(1)
摊余成本

如果我们使用这个伪代码

while priorityQueue not empty
    u = priorityQueue.exractMin()
    for each v in u.adjacencies
        if priorityQueue.contains(v) and needsWeightReduction(u, v)
            priorityQueue.updateKeyWeight(u, v)
假设实现对
priorityQueue.contains(v)
needsWeightReducement(u,v)
都有固定的时间


需要注意的是,为了检查邻接,可以稍微收紧绑定。当外部循环运行
V
次,并且检查任何单个节点的邻接最坏情况是
V
操作时,您可以使用聚合分析来认识到,您将永远不会检查超过
E
的邻接(因为只有E条边)。而
E使用基于最小堆的优先级队列,时间复杂度为O(ElogV)

正如您所说,外部while循环是O(V),因为它通过每个顶点循环,因为每个顶点都需要添加到MST一次

对于while循环中考虑的每个顶点,需要执行以下两个步骤:

  • 选择要添加到MST的下一条边。根据基于最小堆的优先级队列的属性,根元素始终是最小的元素。因此,选择下一条边,即成本最低的边,将是对根元素的O(1)提取。提取后,需要以保持优先级队列的方式移动剩余值。因为队列表示一个平衡的二叉树,所以在最坏的情况下,这种移位可能发生在O(logV)中

  • 优先级队列已更新。新顶点的边缘可能需要在队列中更新它们的成本,因为我们现在将考虑与新添加的顶点及其邻居之间的边缘相关的成本。(但是,如果它们通过比新引入的边成本更低的边与先前添加的顶点相邻,则不会更新成本,因为我们正在寻找最小成本)。同样,这将是O(logV)因为在最坏的情况下,顶点需要在代表队列的平衡二叉树的整个长度上移动

  • 步骤1发生了V次,因为它在while循环中发生了一次,所以总共是O(VlogV),而步骤2发生了E次,在最坏的情况下,每个边都连接到当前顶点,因此它们都需要更新,这意味着总共是O(ElogV)。设置为O(E)因为它需要将优先级队列中的每个边缘成本初始化为无穷大

    使用基于最小堆的优先级队列的总时间复杂度=O(E+VlogV+ElogV)=O(ElogV)

    当您阅读到复杂性为O(V^2)时,您可能会看到不使用堆的实现。在这种情况下,外部while循环仍然是O(V)。瓶颈在于选择要添加到MST的下一个顶点的步骤,即O(V)因为您需要检查与每个相邻节点关联的成本,以找到最低成本,在最坏的情况下,这意味着检查所有其他节点。因此复杂性为O(V*V)=O(V^2)


    此外,在非常稠密的图中,O(ElogV)变为O(V^2),因为在任何图中,总边的最大值为E=V^2。

    因此,选择下一条边,即成本最低的边,将是对根元素的O(1)提取。
    从PQ中提取元素是O(logN),因为之后需要重新平衡。
      V*lgV + E*1
    = O(V lgV + E)
    
      O(V lgV + V^2)
    = O(V^2)