最近有许多人问我关于最小生成树问题,所以在此写一篇博客
提到最小生成树,自然就会想到Kruskal,Kruskal 算法很简单大致就是把所有的结点分到两个集合中去,记录已选过的点和没有选过的点,
对边进行从小到大排序,选出n-1条边,此时的n-1边对应的值就是生成树的权值,即此时是最小生成树。
代码:
#include<stdio.h>
#include<stdlib.h>
struct node{//使用结构体存储边的关系
int u;
int v;
int w;
}e[101];
int n,m,f[101],sum=0,count=0;
int cmp(const void *a,const void *b)//快排
{
return (((struct node *)a)->w>((struct node *)b)->w?1:-1);
}
int getf(int x)//并查集寻找祖先的函数
{
if(f[x]==x)
return x;
else
{
//路径压缩
f[x]=getf(f[x]);
return f[x];
}
}
//并查集合并两个子函数
int merge(int u,int v)
{
int t1,t2;
t1=getf(u);//寻找祖先节点
t2=getf(v);
if(t1!=t2)//判断两个结点是否在同一个集合中
{
f[t2]=t1;
return 1;
}
return 0;
}
int main()
{
int i;
//读入n和m,n表示顶点个数,m表示边的条数
scanf("%d%d",&n,&m);
//读入边,用结构体来存储边的关系
for(i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
//快排,按照从小到大排序
qsort(e,m+1,sizeof(e[0]),cmp);
//初始化并查集
for(i=1;i<=n;i++)
f[i]=i;
//Kruskal算法核心
for(i=1;i<=m;i++)//从小到大枚举每一条边
{
//判断一条边的两个定点是否已经联通,即判断是否在一个集合中
if(merge(e[i].u,e[i].v))// 如果没有联通,选用这条边
{
count++;
sum=sum+e[i].w;
}
//知道选了n-1条边后退出循环
if(count==n-1)
break;
}
//输出最小的值
printf("%d\n",sum);
return 0;
}
Kruskal 算法时间复杂度: 对边进行快速排序是O(MlogM),在m条边中找出n-1条边是O(MlogN),所以Kruskal算法的时间复杂度是 O(MlogM+MlogN);同时由于M要比N大很多 ,因此最终时间复杂度为O(MlogM).