为什么我们需要Prim算法中的优先级队列

时间:2011-08-12 10:06:08

标签: c++ algorithm graph-theory minimum-spanning-tree prims-algorithm

正如我的问题所说,我想知道为什么我们在Prim's Algorithm中使用优先级队列? 它如何使我们无法使用天真的方式(是的,我听说过它,但不知道为什么)。

如果有人能逐步解释邻接列表,我会很高兴。我正在使用Cormen的书。

伪代码:

Prim(G,w,r) //what is w (weight?) and r?
  For each u in V[G]
    do key[u] ← ∞ // what is key?
       π[u] ← NIL  
  key[r] ← 0
  Q ← V[G]  
  While Q ≠ Ø
    do u ← EXTRACT-MIN(Q)
       for each v in Adj[u]
            if v is in Q and w(u,v) < key[v]
                 then π[v] ← u
                       key[v] ← w(u,v)

我正在考虑使用std :: vector然后使用std :: make_heap();作为存储边缘的优先级队列。

4 个答案:

答案 0 :(得分:10)

在prim的算法中,有一个步骤,你必须得到'最近'的顶点。如果使用普通数组,此步骤将花费O(N),但如果使用优先级队列(例如堆),则只需要O(logN)

因此,使用优先级队列的原因是为了减少算法的时间复杂度(这意味着它使程序运行得更快)

**

更新

**

这是来自Wikipedia的Prim算法的描述。粗体部分是找到我所谈到的最近顶点的部分:

输入:具有顶点V和边E的非空连通加权图(权重可以为负)。

初始化:Vnew = {x},其中x是V的任意节点(起始点),Enew = {}

重复直到Vnew = V: 选择具有最小权重的边(u,v),使得u处于Vnew且v不是(如果有多个边具有相同的权重,则可以选择其中任何一个) 将v添加到Vnew,并将(u,v)添加到Enew

输出:Vnew和Enew描述最小生成树

答案 1 :(得分:6)

你不需要它。实际上,Prim算法的简单实现只需对距离数组进行线性搜索即可找到下一个最近的顶点。 Dijkstra的算法完全相同。

人们使用的原因是因为它显着加快了算法的运行时间。它从O(V^2 + E)变为O(E*log(V))

关键是EXTRACT-MIN(Q)功能。如果你天真地这样做,这个操作需要O(V)时间。使用堆,只需O(logV)次。

答案 2 :(得分:3)

大致从内存中执行此操作,因此可能稍微不一致,但它得到了重点:

class Graph
  Set<node> nodes;   // The set of nodes in the graph
  MultiMap<Node, Edge> edges; // Map from Node, to a list of weighted edges connected to the node. If it weren't weighted, any spanning tree by definition would be a minimum spanning tree.

Graph Prim(Graph input):
   Graph MST = new Graph();
   PriorityQueue<Edge> candidateEdges;
   Node anyNode = input.pickAnyNodeAtRandom()
   candidateEdges.putAll(input.edges.get(anyNode));

   while MST.nodes.size() < input.nodes.size():
      edge = candidateEdges.takeLowest()  // THIS IS THE IMPORTANT PART         
      if edge.v1 in MST.nodes and edge.v2 not in MST.nodes:
         MST.nodes.add(edge.v2)       
         MST.edges.add(edge)
         candidateEdges.add(edge.v2.edges)

基本上,在算法的每一步中,您都在寻找最小边缘,其中一个顶点位于部分最小生成树中,一个顶点不在树中,并且您要将所述边添加到树中。你怎么有效地做到这一点?如果您有办法有效地对连接到部分生成树中的顶点的所有边进行排序,则可以简单地遍历它们,直到找到具有可接受顶点的边。

如果没有这样的有序数据结构,您必须每次迭代所有候选边缘以找到最小值,而不是直接有效地获取最小值。

答案 3 :(得分:1)

Prim的算法使用两个集合 - 比如U和V / U.

你是从根开始的,(root是U中唯一的元素)。 您将所有顶点放在队列中,其中weight [v] = dist [root,v]其中v与root相邻。 因此,当您从队列中弹出时,您将获取顶点(假设您),其中一端在U中,以V / U结尾,并且是具有该属性的最小值。 您将其权重,其父级设置为root等等...并将其所有相邻节点放入队列中。所以现在队列中的所有节点都与根相邻,所有节点都与根相邻,所有节点都与u相邻,各自具有相应的权重。 因此,当您从中弹出时,您将再次从V / U获得一个与U'最接近'的节点。

在实现中,他们最初将每个顶点添加到具有INFINITY优先级的队列,但正如您所见,它们正逐渐更新权重。这反映在优先级队列中,保证上面的文本。

希望它有所帮助。