并查集求最小生成树

最近有许多人问我关于最小生成树问题,所以在此写一篇博客

提到最小生成树,自然就会想到Kruskal,Kruskal 算法很简单大致就是把所有的结点分到两个集合中去,记录已选过的点和没有选过的点,

对边进行从小到大排序,选出n-1条边,此时的n-1边对应的值就是生成树的权值,即此时是最小生成树。

代码:

#include
#include
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).

你可能感兴趣的:(并查集求最小生成树)