算法学习笔记(30)——Kruskal算法(最小生成树)

Kruskal 算法

首先,将所有边按照权重从小到大排序。这一步是Kruskal算法的性能瓶颈,时间复杂度为 \(O(m \log m)\) 。不过由于排序的时间复杂度常数很小,所以Kruskal算法实际上是很快的。

第二部从小到大枚举每条边 \(a \rightarrow b\),边权为 \(w\) ,如果 \(a\)\(b\) 目前不在一个连通块中,就把这条边加入最小生成树。该部分可以用并查集来实现。

题目链接:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10, M = 2e5 + 10;

struct Edge
{
    int a, b, w;
    
    // 运算符重载(常量成员函数)
    bool operator< (const Edge &e) const {
        return w < e.w;
    }
}edges[M];

int n, m;
int p[N]; // 并查集

// 并查集查找操作(路径压缩优化)
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void kruskal()
{
    // 首先对所有边按边权从小到大排序
    sort(edges, edges + m);
    
    // 初始化并查集
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    
    // res存储最小生成树的边权之和,cnt存储最小生成树的边数
    int res = 0, cnt = 0;
    
    // 从小到大枚举每条边
    for (int i = 0; i < m; i ++ ) {
        auto e = edges[i];
        int a = e.a, b = e.b, w = e.w;
        // 如果a和b不在同一集合,则将这条边加入最小生成树
        if (find(a) != find(b)) {
            p[find(a)] = find(b);
            res += w;
            cnt ++;
        }
    }
    
    // 根据最小生成树定义判断(n个点的最小生成树的边数一定为n-1)
    if (cnt < n - 1) puts("impossible");
    else cout << res << endl;
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i ++ ) {
        int u, v, w;
        cin >> u >> v >> w;
        edges[i] = {u, v, w};
    }
    
    kruskal();
    
    return 0;
}
posted @ 2022-12-10 09:33  S!no  阅读(33)  评论(0编辑  收藏  举报