与Prim算法不同,Kruskal算法基于并查集和贪心算法,利用并查集判断其是否存在循环。
Kruskal算法基本思想: 对所有边进行排序,优先选取权重小的边,并同时判断其是否联通(利用并查集判断)
数据结构:结构体数组
算法思想:快排,贪心,递归
用结构体来储存边
typedef struct edge{
int u;//顶点
int v;//末点
int w;//权重
}EDGE;
EDGE e[100];
利用快排排序:
void sort(int left,int right)
{
int i,j;
EDGE t;
if(left>right)
return ;
i=left;
j=right;
while(i!=j)
{
while(e[j].w>=e[left].w&&i<j) j--;
while(e[i].w<=e[left].w&&i<j) i++;
if(i<j)
{
t=e[i];
e[i]=e[j];
e[j]=t;
}
}
t=e[i];
e[i]=e[left];
e[left]=t;
sort(left,i-1);
sort(i+1,right);
return ;
}
利用并查集来判断是否是形成联通环:
int merge(int x,int y)
{
int i=getf(x);
int j=getf(y);
if(i!=j)//如果不相同则利用偏左原则
{
f[j]=i;//把祖宗更变
return 1;
}
else
return 0;
}
int getf(int v)//利用递归找祖先
{
if(v==f[v])
return v;
else
{
f[v]=getf(f[v]);
return f[v];
}
}
最后利用贪心把边一条条符合条件的边放入集合中:
for(i=1;i<=m;i++)
{
if(merge(e[i].u,e[i].v))
{
sum+=e[i].w;//如果不在一个集合中,则把边放入
count++;//计数器
}
if(count==n-1)
break;
}
来看全部代码的实现:
#include<stdio.h>
#include<string.h>
#include<conio.h>
int f[100];
int n,m,sum,count;
typedef struct edge{
int u;//顶点
int v;//末点
int w;//权重
}EDGE;
EDGE e[100];
int getf(int v);
void sort(int left,int right);
int merge(int x,int y);
int main()
{
int i,j;
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++) f[i]=i;//初始化并查集
for(i=1;i<=m;i++)
scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
sort(1,m);
for(i=1;i<=m;i++)
{
if(merge(e[i].u,e[i].v))
{
sum+=e[i].w;//如果不在一个集合中,则把边放入
count++;//计数器
}
if(count==n-1)
break;
}
printf("%d",sum);
return 0;
}
void sort(int left,int right)
{
int i,j;
EDGE t;
if(left>right)
return ;
i=left;
j=right;
while(i!=j)
{
while(e[j].w>=e[left].w&&i<j) j--;
while(e[i].w<=e[left].w&&i<j) i++;
if(i<j)
{
t=e[i];
e[i]=e[j];
e[j]=t;
}
}
t=e[i];
e[i]=e[left];
e[left]=t;
sort(left,i-1);
sort(i+1,right);
return ;
}
int merge(int x,int y)
{
int i=getf(x);
int j=getf(y);
if(i!=j)//如果不相同则利用偏左原则
{
f[j]=i;//把祖宗更变
return 1;
}
else
return 0;
}
int getf(int v)//利用递归找祖先
{
if(v==f[v])
return v;
else
{
f[v]=getf(f[v]);
return f[v];
}
}
其实这个算法和prim算法中,都包含了贪心算法的思想,只是实现的方式不同。