最小生成树的概念:给出n个点,选择n-1条边把n个点连接起来,使得n个点在一个连通分量里,并且要使这些边的边权总和最小。
克鲁斯卡尔算法是”加边法“,采用了贪心的策略,把任意两点之间的边权排序,然后依次选择权值最小的边加入到生成树中,直到选出n-1条边算法结束。在选取的过程中如果出现这条边连接的2个点在一个连通分量里,就舍弃这条边,继续下一次选取,因为如果选择了这条边,生成树中就会出现环。
算法的关键在于连通分量的查询与合并:需要知道任意两个点是否在一个连通分量里,还需要合并两个连通分量。这里我们采用的是并查集来维护。
并查集这里不给出过多的介绍,下面贴出2个函数的代码,分别表示寻找连通块中的代表元素并且压缩路径,和合并并查集。
int Find(int x)
{
return p[x]==x?x:p[x]=Find(p[x]);
}////p[x]保存x的父节点,如果x就是代表元素,p[x]=x.
void join(int x,int y)//////合并并查集
{
int px=Find(x),py=Find(y);
if(Find(x)!=Find(y))
px=p[py];////这里的赋值顺序不要写反
}
这样,克鲁斯卡尔算法的代码就不难给出了
struct stu
{
int u,v,w;
}
bool cmp(stu s1,stu s2)
{return s1.w<s2.w;
}
int Kruskal()
{
int value=0;
for(int i=0;i<n;i++)p[i]=i;
sort(node,node+m,cmp);
for(int i=0; i<m&&t<n-1; i++)
{
int p=Find(node[i].u);
int q=Find(node[i].v);
if(p!=q)////这说明加入这条边不会形成环
{
value=value+node[i].w;
p[q]=p;
t++;
}
return value;
}