最近有許多人問我關於最小生成樹問題,所以在此寫一篇博客
提到最小生成樹,自然就會想到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).