Kruskal算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法。
对于给定的一张图,其算法思路:
- 将边按权值升序排序;
- 选出权值最小的边加入到最小生成树中,如果产生回路就放弃这条边(利用并查集来判断是否产生回路);
- 重复步骤2,直到所有顶点都加入最小生成树中;
算法过程图:
将图的边按权值排序,每个顶点放在单独的集合里:
(1)选出权值最小的边(0,2)加入到最小生成树中,顶点0和2不在同一集合,没产生回路。合并顶点0和2所在的集合。
(2)选出权值最小的边(3,5)加入到最小生成树中,顶点3和5不在同一集合,没产生回路。合并顶点3和5所在的集合。
(3)选出权值最小的边(1,4)加入到最小生成树中,顶点1和4不在同一集合,没产生回路。合并顶点1和4所在的集合。
(4)选出权值最小的边(2,5)加入到最小生成树中,顶点2和5不在同一集合,没产生回路。合并顶点2和5所在的集合。
(5)选出权值最小的边(0,3)加入到最小生成树中,顶点0和3在同一集合中,产生回路,故放弃这条边。再选出权值最小的边(1,2)加入到最小生成树中,顶点1和2不在同一集合,没产生回路。合并顶点1和2所在的集合。
至此所有顶点都在同一个集合中,一颗最小生成树就构造完成。
模板函数:
const int INF = 0x3f3f3f3f;
const int N = 55;
int map[N][N]; //邻接矩阵存图
int Set[N]; //并查集
int n; //n个顶点
struct Edge
{
int x,y; //顶点x,y
int weight;//权值
bool operator< (const Edge &x)const //比较权值大小
{
return weight<x.weight;
}
}e[N*N];
void init() //初始化并查集
{
for(int i=1;i<=n;i++)
Set[i]=i;
}
int Find(int x)//查找根节点
{
if(x==Set[x])
return x;
return Set[x]=Find(Set[x]);
}
bool Union(int x,int y)//合并集合
{
x=Find(x);
y=Find(y);
if(x==y)
return false;
if(x>y)
Set[x]=y;
else
Set[y]=x;
return true;
}
int Kruskal(int m)//m条边
{
init(); //初始并查集
sort(e,e+m); //按边权值升序排序
int cnt=0; //统计最小生成树上的边数
int res=0; //累加最小生成树的所有边权值和
for(int i=0;i<m;i++) //遍历所有边
{
if(Union(e[i].x,e[i].y)) //判断两个顶点是否在同一集合(是否构成回路)
{
cnt++; //没有构成回路,最小生成树的边数+1
res+=e[i].weight; //累加权值
}
if(cnt==n-1) //找到最小生成树上的n-1条边说明最小生成树构造完成
return res; //返回权值和
}
return res;
}