📜  Prim的算法在STL中使用priority_queue

📅  最后修改于: 2022-05-13 01:57:54.535000             🧑  作者: Mango

Prim的算法在STL中使用priority_queue

给定一个无向、连通和加权图,使用 Prim 算法找到该图的最小生成树 (MST)。

Input : Adjacency List representation
        of above graph
Output :  Edges in MST
          0 - 1
          1 - 2
          2 - 3
          3 - 4
          2 - 5
          5 - 6
          6 - 7
          2 - 8


Note :  There are two possible MSTs, the other
        MST includes edge 0-7 in place of 1-2. 

我们在下面讨论了 Prim 的 MST 实现。

  • Prim 的邻接矩阵表示算法(在 C/C++ 中,时间复杂度 O(v 2 )
  • Prim 的邻接表表示算法(在 C 中,时间复杂度为 O(ELogV))

第二种实现在时间复杂度方面更好,但确实很复杂,因为我们已经实现了自己的优先级队列。 STL 提供了priority_queue,但是提供的优先级队列不支持减键操作。在 Prim 的算法中,我们需要一个优先级队列以及对优先级队列的以下操作:

  • ExtractMin :从所有尚未包含在 MST 中的顶点中,我们需要获取具有最小键值的顶点。
  • DecreaseKey :提取顶点后,我们需要更新其相邻顶点的键,如果新的键更小,则更新数据结构中的键。

可以修改此处讨论的算法,以便从不需要减少键。这个想法是,不要将所有顶点插入优先级队列中,而只插入那些不是 MST 并且通过已包含在 MST 中的顶点访问过的顶点。我们在单独的布尔数组 inMST[] 中跟踪包含在 MST 中的顶点。

1) Initialize keys of all vertices as infinite and 
   parent of every vertex as -1.

2) Create an empty priority_queue pq.  Every item
   of pq is a pair (weight, vertex). Weight (or 
   key) is used used as first item  of pair
   as first item is by default used to compare
   two pairs.

3) Initialize all vertices as not part of MST yet.
   We use boolean array inMST[] for this purpose.
   This array is required to make sure that an already
   considered vertex is not included in pq again. This
   is where Prim's implementation differs from Dijkstra.
   In Dijkstra's algorithm, we didn't need this array as
   distances always increase. We require this array here 
   because key value of a processed vertex may decrease
   if not checked.

4) Insert source vertex into pq and make its key as 0.

5) While either pq doesn't become empty 
    a) Extract minimum key vertex from pq. 
       Let the extracted vertex be u.

    b) Include u in MST using inMST[u] = true.

    c) Loop through all adjacent of u and do 
       following for every vertex v.

           // If weight of edge (u,v) is smaller than
           // key of v and v is not already in MST
           If inMST[v] = false && key[v] > weight(u, v)

               (i) Update key of v, i.e., do
                     key[v] = weight(u, v)
               (ii) Insert v into the pq 
               (iv) parent[v] = u
               
6) Print MST edges using parent array.

下面是上述想法的 C++ 实现。

C++
// STL implementation of Prim's algorithm for MST
#include
using namespace std;
# define INF 0x3f3f3f3f
 
// iPair ==>  Integer Pair
typedef pair iPair;
 
// This class represents a directed graph using
// adjacency list representation
class Graph
{
    int V;    // No. of vertices
 
    // In a weighted graph, we need to store vertex
    // and weight pair for every edge
    list< pair > *adj;
 
public:
    Graph(int V);  // Constructor
 
    // function to add an edge to graph
    void addEdge(int u, int v, int w);
 
    // Print MST using Prim's algorithm
    void primMST();
};
 
// Allocates memory for adjacency list
Graph::Graph(int V)
{
    this->V = V;
    adj = new list [V];
}
 
void Graph::addEdge(int u, int v, int w)
{
    adj[u].push_back(make_pair(v, w));
    adj[v].push_back(make_pair(u, w));
}
 
// Prints shortest paths from src to all other vertices
void Graph::primMST()
{
    // Create a priority queue to store vertices that
    // are being primMST. This is weird syntax in C++.
    // Refer below link for details of this syntax
    // http://geeksquiz.com/implement-min-heap-using-stl/
    priority_queue< iPair, vector  , greater > pq;
 
    int src = 0; // Taking vertex 0 as source
 
    // Create a vector for keys and initialize all
    // keys as infinite (INF)
    vector key(V, INF);
 
    // To store parent array which in turn store MST
    vector parent(V, -1);
 
    // To keep track of vertices included in MST
    vector inMST(V, false);
 
    // Insert source itself in priority queue and initialize
    // its key as 0.
    pq.push(make_pair(0, src));
    key[src] = 0;
 
    /* Looping till priority queue becomes empty */
    while (!pq.empty())
    {
        // The first vertex in pair is the minimum key
        // vertex, extract it from priority queue.
        // vertex label is stored in second of pair (it
        // has to be done this way to keep the vertices
        // sorted key (key must be first item
        // in pair)
        int u = pq.top().second;
        pq.pop();
         
          //Different key values for same vertex may exist in the priority queue.
          //The one with the least key value is always processed first.
          //Therefore, ignore the rest.
          if(inMST[u] == true){
            continue;
        }
       
        inMST[u] = true;  // Include vertex in MST
 
        // 'i' is used to get all adjacent vertices of a vertex
        list< pair >::iterator i;
        for (i = adj[u].begin(); i != adj[u].end(); ++i)
        {
            // Get vertex label and weight of current adjacent
            // of u.
            int v = (*i).first;
            int weight = (*i).second;
 
            //  If v is not in MST and weight of (u,v) is smaller
            // than current key of v
            if (inMST[v] == false && key[v] > weight)
            {
                // Updating key of v
                key[v] = weight;
                pq.push(make_pair(key[v], v));
                parent[v] = u;
            }
        }
    }
 
    // Print edges of MST using parent array
    for (int i = 1; i < V; ++i)
        printf("%d - %d\n", parent[i], i);
}
 
// Driver program to test methods of graph class
int main()
{
    // create the graph given in above figure
    int V = 9;
    Graph g(V);
 
    //  making above shown graph
    g.addEdge(0, 1, 4);
    g.addEdge(0, 7, 8);
    g.addEdge(1, 2, 8);
    g.addEdge(1, 7, 11);
    g.addEdge(2, 3, 7);
    g.addEdge(2, 8, 2);
    g.addEdge(2, 5, 4);
    g.addEdge(3, 4, 9);
    g.addEdge(3, 5, 14);
    g.addEdge(4, 5, 10);
    g.addEdge(5, 6, 2);
    g.addEdge(6, 7, 1);
    g.addEdge(6, 8, 6);
    g.addEdge(7, 8, 7);
 
    g.primMST();
 
    return 0;
}


C++
// STL implementation of Prim's algorithm for MST
#include
using namespace std;
# define INF 0x3f3f3f3f
 
// iPair ==> Integer Pair
typedef pair iPair;
 
// To add an edge
void addEdge(vector  > adj[], int u,
                                     int v, int wt)
{
    adj[u].push_back(make_pair(v, wt));
    adj[v].push_back(make_pair(u, wt));
}
 
// Prints shortest paths from src to all other vertices
void primMST(vector > adj[], int V)
{
    // Create a priority queue to store vertices that
    // are being primMST. This is weird syntax in C++.
    // Refer below link for details of this syntax
    // http://geeksquiz.com/implement-min-heap-using-stl/
    priority_queue< iPair, vector  , greater > pq;
 
    int src = 0; // Taking vertex 0 as source
 
    // Create a vector for keys and initialize all
    // keys as infinite (INF)
    vector key(V, INF);
 
    // To store parent array which in turn store MST
    vector parent(V, -1);
 
    // To keep track of vertices included in MST
    vector inMST(V, false);
 
    // Insert source itself in priority queue and initialize
    // its key as 0.
    pq.push(make_pair(0, src));
    key[src] = 0;
 
    /* Looping till priority queue becomes empty */
    while (!pq.empty())
    {
        // The first vertex in pair is the minimum key
        // vertex, extract it from priority queue.
        // vertex label is stored in second of pair (it
        // has to be done this way to keep the vertices
        // sorted key (key must be first item
        // in pair)
        int u = pq.top().second;
        pq.pop();
 
          //Different key values for same vertex may exist in the priority queue.
          //The one with the least key value is always processed first.
          //Therefore, ignore the rest.
          if(inMST[u] == true){
            continue;
        }
       
        inMST[u] = true; // Include vertex in MST
 
        // Traverse all adjacent of u
        for (auto x : adj[u])
        {
            // Get vertex label and weight of current adjacent
            // of u.
            int v = x.first;
            int weight = x.second;
 
            // If v is not in MST and weight of (u,v) is smaller
            // than current key of v
            if (inMST[v] == false && key[v] > weight)
            {
                // Updating key of v
                key[v] = weight;
                pq.push(make_pair(key[v], v));
                parent[v] = u;
            }
        }
    }
 
    // Print edges of MST using parent array
    for (int i = 1; i < V; ++i)
        printf("%d - %d\n", parent[i], i);
}
 
// Driver program to test methods of graph class
int main()
{
    int V = 9;
    vector adj[V];
 
    // making above shown graph
    addEdge(adj, 0, 1, 4);
    addEdge(adj, 0, 7, 8);
    addEdge(adj, 1, 2, 8);
    addEdge(adj, 1, 7, 11);
    addEdge(adj, 2, 3, 7);
    addEdge(adj, 2, 8, 2);
    addEdge(adj, 2, 5, 4);
    addEdge(adj, 3, 4, 9);
    addEdge(adj, 3, 5, 14);
    addEdge(adj, 4, 5, 10);
    addEdge(adj, 5, 6, 2);
    addEdge(adj, 6, 7, 1);
    addEdge(adj, 6, 8, 6);
    addEdge(adj, 7, 8, 7);
 
    primMST(adj, V);
 
    return 0;
}


C++
// If v is not in MST and weight of (u,v) is smaller
 // than current key of v
 if (inMST[v] == false && key[v] > weight)
 {
      // Updating key of v
      key[v] = weight;
      pq.push(make_pair(key[v], v));
      parent[v] = u;
}


Java
// If v is not in MST and weight of (u,v) is smaller
// than current key of v
  
if (inMST[v] == false && key[v] > weight)
{
   
  // Updating key of v
  key[v] = weight;
  pq.add(new Pair(key[v], v));
  parent[v] = u;
}
  
// This code is contributed by avanitrachhadiya2155


Python3
# If v is not in MST and weight of (u,v) is smaller
# than current key of v
 
if (inMST[v] == False and key[v] > weight) :
 
  # Updating key of v
  key[v] = weight
  pq.append([key[v], v])
  parent[v] = u
   
  # This code is contributed by divyeshrabadiya07.


C#
// If v is not in MST and weight of (u,v) is smaller
// than current key of v
 
if (inMST[v] == false && key[v] > weight)
{
  // Updating key of v
  key[v] = weight;
  pq.Add(new Tuple(key[v], v));
  parent[v] = u;
}
 
// This code is contributed by divyesh072019.


Javascript


时间复杂度:O(E Log V))

使用加权图的向量表示的更快实现

C++

// STL implementation of Prim's algorithm for MST
#include
using namespace std;
# define INF 0x3f3f3f3f
 
// iPair ==> Integer Pair
typedef pair iPair;
 
// To add an edge
void addEdge(vector  > adj[], int u,
                                     int v, int wt)
{
    adj[u].push_back(make_pair(v, wt));
    adj[v].push_back(make_pair(u, wt));
}
 
// Prints shortest paths from src to all other vertices
void primMST(vector > adj[], int V)
{
    // Create a priority queue to store vertices that
    // are being primMST. This is weird syntax in C++.
    // Refer below link for details of this syntax
    // http://geeksquiz.com/implement-min-heap-using-stl/
    priority_queue< iPair, vector  , greater > pq;
 
    int src = 0; // Taking vertex 0 as source
 
    // Create a vector for keys and initialize all
    // keys as infinite (INF)
    vector key(V, INF);
 
    // To store parent array which in turn store MST
    vector parent(V, -1);
 
    // To keep track of vertices included in MST
    vector inMST(V, false);
 
    // Insert source itself in priority queue and initialize
    // its key as 0.
    pq.push(make_pair(0, src));
    key[src] = 0;
 
    /* Looping till priority queue becomes empty */
    while (!pq.empty())
    {
        // The first vertex in pair is the minimum key
        // vertex, extract it from priority queue.
        // vertex label is stored in second of pair (it
        // has to be done this way to keep the vertices
        // sorted key (key must be first item
        // in pair)
        int u = pq.top().second;
        pq.pop();
 
          //Different key values for same vertex may exist in the priority queue.
          //The one with the least key value is always processed first.
          //Therefore, ignore the rest.
          if(inMST[u] == true){
            continue;
        }
       
        inMST[u] = true; // Include vertex in MST
 
        // Traverse all adjacent of u
        for (auto x : adj[u])
        {
            // Get vertex label and weight of current adjacent
            // of u.
            int v = x.first;
            int weight = x.second;
 
            // If v is not in MST and weight of (u,v) is smaller
            // than current key of v
            if (inMST[v] == false && key[v] > weight)
            {
                // Updating key of v
                key[v] = weight;
                pq.push(make_pair(key[v], v));
                parent[v] = u;
            }
        }
    }
 
    // Print edges of MST using parent array
    for (int i = 1; i < V; ++i)
        printf("%d - %d\n", parent[i], i);
}
 
// Driver program to test methods of graph class
int main()
{
    int V = 9;
    vector adj[V];
 
    // making above shown graph
    addEdge(adj, 0, 1, 4);
    addEdge(adj, 0, 7, 8);
    addEdge(adj, 1, 2, 8);
    addEdge(adj, 1, 7, 11);
    addEdge(adj, 2, 3, 7);
    addEdge(adj, 2, 8, 2);
    addEdge(adj, 2, 5, 4);
    addEdge(adj, 3, 4, 9);
    addEdge(adj, 3, 5, 14);
    addEdge(adj, 4, 5, 10);
    addEdge(adj, 5, 6, 2);
    addEdge(adj, 6, 7, 1);
    addEdge(adj, 6, 8, 6);
    addEdge(adj, 7, 8, 7);
 
    primMST(adj, V);
 
    return 0;
}

注意:像 Dijkstra 的 priority_queue 实现一样,我们可能对同一个顶点有多个条目,因为我们没有(而且我们不能)在 if 条件下使 isMST[v] = true 。

C++

// If v is not in MST and weight of (u,v) is smaller
 // than current key of v
 if (inMST[v] == false && key[v] > weight)
 {
      // Updating key of v
      key[v] = weight;
      pq.push(make_pair(key[v], v));
      parent[v] = u;
}

Java

// If v is not in MST and weight of (u,v) is smaller
// than current key of v
  
if (inMST[v] == false && key[v] > weight)
{
   
  // Updating key of v
  key[v] = weight;
  pq.add(new Pair(key[v], v));
  parent[v] = u;
}
  
// This code is contributed by avanitrachhadiya2155

Python3

# If v is not in MST and weight of (u,v) is smaller
# than current key of v
 
if (inMST[v] == False and key[v] > weight) :
 
  # Updating key of v
  key[v] = weight
  pq.append([key[v], v])
  parent[v] = u
   
  # This code is contributed by divyeshrabadiya07.

C#

// If v is not in MST and weight of (u,v) is smaller
// than current key of v
 
if (inMST[v] == false && key[v] > weight)
{
  // Updating key of v
  key[v] = weight;
  pq.Add(new Tuple(key[v], v));
  parent[v] = u;
}
 
// This code is contributed by divyesh072019.

Javascript


但正如 Dijkstra 算法中所解释的,时间复杂度仍然为 O(E Log V),因为优先级队列中最多有 O(E) 个顶点,O(Log E) 与 O(Log V) 相同。

与 Dijkstra 的实现不同,布尔数组 inMST[] 在这里是强制性的,因为新插入的项的键值可能小于提取项的键值。所以我们不能考虑提取的项目。