Kruskal 算法

Kruskal 算法

1.Kruskal 算法介绍

最小生成树:

给定一张边带权的无向图 \(G=(V,E)\),其中 \(V\) 表示图中点的集合,\(E\) 表示图中边的集合,\(n=|V|\)\(m=|E|\)

\(V\) 中的全部 \(n\) 个顶点和 \(E\)\(n-1\) 条边构成的无向连通子图被称为 \(G\) 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 \(G\) 的最小生成树。

Kruskal 算法:

Kruskal 算法是图论最小生成树问题算法的一种,利用并查集的方式实现,多用于稀疏图,时间复杂度为 \(O(mlogm)\)\(m\) 为边数。

2.Kruskal 算法例题

给定一个 \(n\) 个点 \(m\) 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

输入格式

第一行包含两个整数 \(n\)\(m\)

接下来 \(m\) 行,每行包含三个整数 \(u,v,w\),表示点 \(u\) 和点 \(v\) 之间存在一条权值为 \(w\) 的边。

输出格式

共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围

\(1≤n≤105\),
\(1≤m≤2*105\),
图中涉及边的边权的绝对值均不超过 \(1000\)

输入样例:

4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4

输出样例:

6

注:此题目来源于AcWing题库第859题

3.Kruskal 算法思路

Kruskal$算法将一个连通块当做一个集合。

Kruskal首先将所有的边按从小到大顺序排序(一般使用快排),并认为每一个点都是孤立的,分属于 \(n\) 个独立的集合。然后按顺序枚举每一条边。

如果这条边连接着两个不同的集合,那么就把这条边加入最小生成树,这两个不同的集合就合并成了一个集合;

如果这条边连接的两个点属于同一集合,就跳过。直到选取了 \(n-1\)条边为止。

4.Kruskal 算法原理

Kruskal 算法每次都选择一条最小的,且能合并两个不同集合的边,一张 \(n\) 个点的图总共选取 \(n-1\) 次边。

因为每次我们选的都是最小的边,所以最后的生成树一定是最小生成树。每次我们选的边都能够合并两个集合,最后 \(n\) 个点一定会合并成 一个集合。

通过这样的贪心策略,Kruskal算法就能得到一棵有 \(n-1\) 条边,连接着 \(n\) 个点的最小生成树。

注:来源于网络

5.Kruskal 算法代码(注释已全部标出)

#include<iostream>
#include<algorithm>
using namespace std;
const int N=100005,M=200005;
int n,m;
int f[N];
struct Edge{
    int a,b,w;
    bool operator< (const Edge &W)const{
        return w<W.w;
    }//重载一下小于号,如果不会可以去看cmp函数
}edges[M];
/*
bool cmp(Edge a,Edge b){
    return a.w<b.w;
}
*/
int find(int x){
    if(f[x]!=x) f[x]=find(f[x]);//如果x非父节点,就继续找
    return x;//是则返回x
} 
int kruskal(){
    int res=0,cnt=0;//res记录最小生成树的树边权重之和,cnt记录的是全部加入到树的集合中边的数量(可能有多个集合)
    for(int i=1;i<=m;i++){
        int a=edges[i].a,b=edges[i].b,w=edges[i].w;
        a=find(a),b=find(b);
        if(a!=b){
            f[b]=a;//将a,b所在的两个集合连接起来
            res+=w;//因为加入的是a-b的这一条边,将a,b所在的两个集合连接之后,全部集合中的边数加1
            cnt++;//加入到集合中的边的权重之和
        }	
    }
    if(cnt<n-1) return 0x3f3f3f3f;
    return res;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++)
        cin>>edges[i].a>>edges[i].b>>edges[i].w;
    sort(edges+1,edges+m+1);//将边的权重按照大小一一排序
    //sort(edges+1,edges+m+1,cmp)
    for(int i=1;i<=n;i++) f[i]=i;//初始化并查集
    int t=kruskal();
    if(t==0x3f3f3f3f) cout<<"impossible";
    else cout<<t;
    return 0;
}

完awa~

109 行!

如有错误请在评论区积极指出。

觉得还行就点个赞吧,您的支持是本蒟蒻最大的动力。

posted @ 2022-07-24 22:41  Rainforests  阅读(459)  评论(1编辑  收藏  举报