📜  在 C++ 中使用 STL 的 Kruskal 的最小生成树

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

在 C++ 中使用 STL 的 Kruskal 的最小生成树

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

Kruskal's Minimum Spanning Tree using STL in C++输入:作为边数组的图形输出:MST 的边为 6 - 7 2 - 8 5 - 6 0 - 1 2 - 5 2 - 3 0 - 7 3 - 4 MST 的权重为 37注意:有两种可能的 MST ,另一个 MST 包括边缘 1-2 代替 0-7。

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

贪心算法 | Set 2(Kruskal 的最小生成树算法)

以下是使用 Kruskal 算法查找 MST 的步骤

  1. 按权重的非递减顺序对所有边进行排序。
  2. 选择最小的边。检查它是否与到目前为止形成的生成树形成一个循环。如果没有形成循环,则包括该边。否则,丢弃它。
  3. 重复步骤#2,直到生成树中有 (V-1) 条边。

以下是对我们使用 STL 实现 Kruskal 算法有用的一些关键点。

  1. 使用由图中所有边组成的边向量,向量的每个项目将包含 3 个参数:源、目标以及源和目标之间的边的成本。
    vector > > edges;

    在外部对(即 pair > )中,第一个元素对应于一条边的成本,而第二个元素本身就是一对,它包含两个边的顶点。

  2. 使用内置的 std::sort 以非递减顺序对边进行排序;默认情况下,排序函数按非降序排序。
  3. 我们使用Union Find Algorithm来检查如果它被添加到当前MST中,当前边缘是否形成一个循环。如果是丢弃它,否则包括它(联合)。

伪代码:

// Initialize result
mst_weight = 0

// Create V single item sets
for each vertex v
    parent[v] = v;
    rank[v] = 0;

Sort all edges into non decreasing 
order by weight w

for each (u, v) taken from the sorted list  E
    do if FIND-SET(u) != FIND-SET(v)
        print edge(u, v)
        mst_weight += weight of edge(u, v)
        UNION(u, v)

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

// C++ program for Kruskal's algorithm to find Minimum
// Spanning Tree of a given connected, undirected and
// weighted graph
#include
using namespace std;
  
// Creating shortcut for an integer pair
typedef  pair iPair;
  
// Structure to represent a graph
struct Graph
{
    int V, E;
    vector< pair > edges;
  
    // Constructor
    Graph(int V, int E)
    {
        this->V = V;
        this->E = E;
    }
  
    // Utility function to add an edge
    void addEdge(int u, int v, int w)
    {
        edges.push_back({w, {u, v}});
    }
  
    // Function to find MST using Kruskal's
    // MST algorithm
    int kruskalMST();
};
  
// To represent Disjoint Sets
struct DisjointSets
{
    int *parent, *rnk;
    int n;
  
    // Constructor.
    DisjointSets(int n)
    {
        // Allocate memory
        this->n = n;
        parent = new int[n+1];
        rnk = new int[n+1];
  
        // Initially, all vertices are in
        // different sets and have rank 0.
        for (int i = 0; i <= n; i++)
        {
            rnk[i] = 0;
  
            //every element is parent of itself
            parent[i] = i;
        }
    }
  
    // Find the parent of a node 'u'
    // Path Compression
    int find(int u)
    {
        /* Make the parent of the nodes in the path
           from u--> parent[u] point to parent[u] */
        if (u != parent[u])
            parent[u] = find(parent[u]);
        return parent[u];
    }
  
    // Union by rank
    void merge(int x, int y)
    {
        x = find(x), y = find(y);
  
        /* Make tree with smaller height
           a subtree of the other tree  */
        if (rnk[x] > rnk[y])
            parent[y] = x;
        else // If rnk[x] <= rnk[y]
            parent[x] = y;
  
        if (rnk[x] == rnk[y])
            rnk[y]++;
    }
};
  
 /* Functions returns weight of the MST*/ 
  
int Graph::kruskalMST()
{
    int mst_wt = 0; // Initialize result
  
    // Sort edges in increasing order on basis of cost
    sort(edges.begin(), edges.end());
  
    // Create disjoint sets
    DisjointSets ds(V);
  
    // Iterate through all sorted edges
    vector< pair >::iterator it;
    for (it=edges.begin(); it!=edges.end(); it++)
    {
        int u = it->second.first;
        int v = it->second.second;
  
        int set_u = ds.find(u);
        int set_v = ds.find(v);
  
        // Check if the selected edge is creating
        // a cycle or not (Cycle is created if u
        // and v belong to same set)
        if (set_u != set_v)
        {
            // Current edge will be in the MST
            // so print it
            cout << u << " - " << v << endl;
  
            // Update MST weight
            mst_wt += it->first;
  
            // Merge two sets
            ds.merge(set_u, set_v);
        }
    }
  
    return mst_wt;
}
  
// Driver program to test above functions
int main()
{
    /* Let us create above shown weighted
       and undirected graph */
    int V = 9, E = 14;
    Graph g(V, E);
  
    //  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);
  
    cout << "Edges of MST are \n";
    int mst_wt = g.kruskalMST();
  
    cout << "\nWeight of MST is " << mst_wt;
  
    return 0;
}

输出 :

Edges of MST are 
          6 - 7
          2 - 8
          5 - 6
          0 - 1
          2 - 5
          2 - 3
          0 - 7
          3 - 4
          
          Weight of MST is 37

优化:
上面的代码可以优化为在选定边数变为 V-1 时停止 Kruskal 的主循环。我们知道 MST 有 V-1 条边,选择了 V-1 条边后没有点迭代。我们没有添加此优化以保持代码简单。

参考:
Cormen Leiserson Rivest 和 Stein(CLRS) 的算法介绍 3

时间复杂度和分步说明在之前关于 Kruskal 算法的文章中进行了讨论。