(算法基础)Kruskal算法

Kruskal算法主要用于解决图论中的最小生成树问题,尤其适用于稀疏图。其主要步骤包括初始化并查集,按边权重排序,通过并查集判断边是否形成环,逐步添加边至最小生成树。时间复杂度为O(MlogM),其中M为边的数量。如果构建过程中连接的节点数少于n-1(n为节点数),则表示不存在最小生成树。
摘要由CSDN通过智能技术生成

适用情景

  1. Kruskal算法主要运用在最小生成树问题当中,适用的情景是在稀疏图(n ~ m)的情况之下,对于稠密图而言用prim算法。

时间复杂度

  1. 因为并查集的话是很快的,它时间复杂度几乎接近于O( 1 ),然后kruskal算法主要的消耗就在排序上面,所以时间复杂度为MlogM,但是在实际过程当中非常快

算法解释

  1. 在这个算法当中需要用到之前的并查集知识,因此在一开始的话,需要写好并查集的find函数,并且对于并查集的parent的数组需要初始化一下,也就是说一开始的话是把每一个点都当成是一个孤立的集合
int parent[N];
int find(int x)
{
    if (parent[x]!=x)
    {
        parent[x]=find(parent[x]);
    }
    return parent[x];
}
for (int i=1;i<=n;i++)  //n表示点的数量
{
    parent[i]=i;
}
  1. 然后在这个算法里面不需要用邻接表或者邻接矩阵去存图,因为在这个算法里面,只需要去枚举每一条边就可以,因此对于边的存储只需要用一个结构体数组就可以
typedef struct edge
{
    int a;
    int b;
    int w;
}edge;
edge arr[m];
  1. 先呢把图里面的每一条边都存到结构体数组里面去
edge arr[m];
int aa,bb,cc;
for (int i=0;i<m;i++)
{
    scanf("%d %d %d",&aa,&bb,&cc);
    arr[i].a=aa;
    arr[i].b=bb;
    arr[i].c=cc;
}
  1. 把每一条边存到结构体数组之后,需要把所有边按权重给它从小到大排序一下
int cmp(const void* e1, const void* e2)
{
    edge* p1=(edge*)e1;
    edge* p2=(edge*)e2;
    return  (p1->w)-(p2->w);
}
qsort(arr,m,sizeof(edge),cmp);
  1. 还要创建一个变量cnt,这个变量主要用来记录一下当前已经加入了多少条边到这个联通块集合(这个联通块集合不断壮大的话,最后就是我要求的最小生成树)当中.
int cnt=0;
  1. 然后这个结构体数组就已经按所有边的权重从小到大已经排序好了,然后就是从前往后枚举每一条边,每次枚举的时候,对于你枚举的每一条边(a点指向b点,权重为w),去判断一下a,b这两个点到底有没有联通?如果说这两个点还没有联通的话,把他们放到一个集合里面,就是说把他们联通起来,然后整个最小生成树的边的总权重和去加上这条边的权重,相当于就是说该边已经被加入到最小生成树当中,所以说变量cnt的话也需要加1
int res=0;
for (int i=0;i<m;i++)
{
    int a=arr[i].a;
    int b=arr[i].b;
    int w=arr[i].w;
    if (find(a)!=find(b))
    {
        cnt++;
        res+=w;
        parent[find(a)]=find(b);
    }
}
  1. 当把每一条边从小到大这么枚举完了之后,当循环退出来的时候,去判断一下这个cnt,因为如果你这个无向图的最小生成树它是存在的话,根据最小生成树以及树形数据结构的定义,这棵树必须得包含原先图的所有的n个点,且得有n-1条边,所以说就去判断一下这个cnt变量是否等于n-1,如果说小于n-1的话,就说明不存在最小生成树
if (cnt<n-1)
{
     printf("impossible\n");
 }
 else
 {
     printf("%d\n",res);
 }

例题

来源:AcWing
Kruskal
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#define N 100010
#define M 200020
typedef struct edge
{
    int a;
    int b;
    int w;
}edge;
int parent[N];
int find(int x)
{
    if (parent[x]!=x)
    {
        parent[x]=find(parent[x]);
    }
    return parent[x];
}
int cmp(const void* e1, const void* e2)
{
    edge* p1=(edge*)e1;
    edge* p2=(edge*)e2;
    return  (p1->w)-(p2->w);
}
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        parent[i]=i;
    }
    edge arr[m];
    int aa,bb,ww;
    for (int i=0;i<m;i++)
    {
        scanf("%d %d %d",&aa,&bb,&ww);
        arr[i].a=aa;
        arr[i].b=bb;
        arr[i].w=ww;
    }
    qsort(arr,m,sizeof(edge),cmp);
    int cnt=0;
    int res=0;
    for (int i=0;i<m;i++)
    {
        int a=arr[i].a;
        int b=arr[i].b;
        int w=arr[i].w;
        if (find(a)!=find(b))
        {
            cnt++;
            res+=w;
            parent[find(a)]=find(b);
        }
    }
    if (cnt<n-1)
    {
        printf("impossible\n");
    }
    else
    {
        printf("%d\n",res);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

絕知此事要躬行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值