並查集求最小生成樹

最近有許多人問我關於最小生成樹問題,所以在此寫一篇博客

提到最小生成樹,自然就會想到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).

發佈了92 篇原創文章 · 獲贊 38 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章