kruskal (克鲁斯卡尔) 算法与前面的 prim(普里姆)算法都是求最小生成树的算法,prim 在没有任何优化的情况下时间复杂度为O(n^2) kruskal 的时间复杂度与使用的排序算法有关 若用快排(qsort) 时间复杂度为 O(N log N) ;两者还有一个区别:prim
算法 以点为单位采用迭代的思想逐步生成最小树,kruskal 算法以边为单位 借助并查集的思想和操作 生成最小树
简单介绍一下kruskal算法:首先将所有的边进行一遍排序 (递增顺序)然后从最小的一条边开始 ,若将此边加入到最小生成树的集合中不会产生回路就将其添加进去直到 存入了 n-1 条边结束。
特别需要注意的是最后要判断一下并查集有几个连通集若大于一则说明没有最小生成树
实现代码:
//kruskal 算法
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXN 100
typedef struct{ //定义一个储存边的结构体
int a;
int b;
int value;
}edge;
int pre[MAXN]; //并查集
int find(int a) //这是并查集查找祖先的函数
{
if (pre[a] == a)
return a;
else return pre[a] = find(pre[a]); //路径压缩
}
void join(int a,int b){ //这是并查集合并祖先的函数
int fa = find(a);
int fb = find(b);
if (fa == fb)
return ;
pre[fa] = fb;
}
void init() //初始化并查集
{
int i;
for (i=0;i<MAXN;i++)
pre[i] = i;
return ;
}
int cmp (void const *a,void const *b){ //qsort() 所要用到的比较函数,是一个升序的比较函数
int va = ((pedge)a)->value;
int vb = ((pedge)b)->value;
return va - vb;
}
void kruskal (int n,int m){ //kruscal 函数
int i, start, end, value, sum = 0,flag = 0; // i 是循环变量,start 和 end 是一条边的两个端点,value 是边的权值,sum 记录最小生成树的权值的和 ,flag 用来判断是否生成了最下生成树;
edge edges[MAXN]; //用来存边的结构体数组
init(); //调用初始化函数
for (i=0;i<m;i++) //将边逐个输入近数组
{
scanf("%d%d%d",&start,&end,&value);
edges[i].a = start;
edges[i].b = end;
edges[i].value = value;
}
qsort(edges,m,sizeof(edges[0]),cmp); //调用 qsort 函数 将边按权值排成升序序列
for (i=0;i<m;i++)
{
if (find(edges[i].a) != find(edges[i].b)) //若边的两个端点在不同的集合中就能将边添入
{
sum += edges[i].value; //将添入的边的权值相加
join (edges[i].a,edges[i].b);
}
}
for (i=1;i<=n;i++) //若并查集中只有一个集合那就说明生成了 最小生成树
{
if (find(i) == i)
flag++;
}
if (flag > 1)
printf("-1\n");
else
printf("%d\n",sum);
}
int main (){
int n, m;
scanf("%d%d",&n,&m);
kruskal(n,m);
}