Kruskal算法是以边为主导地位,始终都是选择当前的可用的最小权值的边。
其主要思想为
1:设一个n个顶点的连通网络为G(V,E),最初先构造一个只有n个顶点没有边的非连通图T=(V,空集),每个顶点自成一格连通分量。
2:当在E中选择一条具有最小权值的边时,若该边的两个顶点落在不同的连通分量上就将此边加入T中,否则就舍去并且以后永久的不使用这条边,重新选择一条权值最小的边。
3:如此重复,直到所有的顶点在同一个连通分量上。
Kruskal算法在没选择一条边加入到生成树的集合T中时有两个关键步骤如下:
1:从E中选择当前权值最小的边,实现时可以用最小堆来存放E中的所有的边或者使用结构体将所有边的信息(边的两个顶点和权值)存放在一个结构体类型的Edge数组中,并按权值的大小从小到大排序,之后按顺序选用每条边
2:选择最小权值的边后,要判断两个顶点是否属于同一个连通分量上,如果是则舍去,如果不是则选用并将这两个顶点分别所在的连通分量合并成一个连通分量。在实现时可以使用并查集来判断两个顶点是否属于同一个连通分量以及将两个连通分量合并成一个连通分量。
接下来介绍并查集:
并查集就是用来解决两个元素是否属于同一个集合,以及把两个集合合并成一个集合
并查集在处理时有搜索和合并两个运算,为了实现并查集的描述和实现,通常把先后加入到集合中的元素表示成一个树结构,并用根节点的序号来代表这个集合,为此定义了一个数组parent[],例如parent[4]=5,意思是4号节点的父亲是5号节点,而parent[7]=-4的意思是7号节点该集合的根节点并且这个集合有4个元素
实现并查集主要有3个函数,代码如下:
一:
void UFset()//初始化
{
for(i=1;i<=n;i++)
parent[i]=-1;
}
二:
int Find(int x)//查找并返回节点x所属集合的根节点
{
int s;//查找位置
for(s=x;parent[s]>=0;s=parent[s]);
while(s!=x)//优化方案...压缩路径,使后续的查找操作加速
{
int tmp=parent[x];
parent[x]=s;
x=tmp;
}
return s;
}
所谓的压缩路径就是增加一个while循环,每次把从节点x到集合的根节点的路径上的结点直接设置为根节点的子女节点。虽然增加了时间,但后续的查找会更快
三:
//将两个不同的集合的元素进行合并,使两个集合中任意的两个元素都连通
void Union(int R1,int R2)
{
int r1=Find(R1),r2=Find(R2);//r1为R1的根节点,r2为R2的根节点
int tmp=parent[r1]+parent[r2];//两个集合的结点个数之和(为负数)
//如果R2所在的树结点的个数》R1所在的树结点的个数(注意parent[r1]是负数)
if(parent[r1]>parent[r2])//优化方案.....加权法则
{
parent[r1]=r2;//将r1所在的树作为r2的子树
parent[r2]=tmp;//更新r2的parent[]的值
}
else
{
parent[r2]=r1;//将r2所在的树的作为r1的子树
parent[r1]=tmp;//更新r1的parent[]的值
}
}
接下来就是Kruskal算法了 主要代码如下:
void Kruskal()
{
int sumweight=0;//生成树的权值
int num=0;//已选用的边的数目
int u,v;
UFset();//初始化parent【】
for(i=0;i<m;i++)
{
u=edges[i].u;
v=edges[i].v;
if(Find(u)!=Find(v))//判断是否属于同一连通分量
{
cout<<u<<' '<<v<<' '<<edges[i].w<<endl;
sumweight+=edges[i].w;
num++;
Union(u,v);//合并两个集合
}
if(num>=n-1)
break;
}
cout<<"Weight of MST is "<<sumweight<<endl;
}
完美补充:
一:
边的表示
struct edge{//边
int u,v,w;//边的顶点,权值
}Edges[MAXM];
二:边的权值按小到大排序使用sort函数
int cmp(edge a,edge b)
{
return a.w<b.w;
}