Kruskal算法思想
G=(V,E)是无向连通网,T=(U,TE)是G的最小生成树算法的基本思想是:初始状态为U=V、TE={ },即T中的顶点各自构成一个连通分量,然后按照边的权值由小到大的顺序,依次考察边E中的各条边。若被考察边的两个顶点属于两个不同的连通分量,则将此边加入到TE中,同时把两个连通分量连接为一个连通分量;若被考察边的两个顶点属于同一个连通分量,则舍去此边,以免造成回路,如此下去,当T中的连通分量个数为1时,此连通分量便为G的一棵最小 Kruskal生成树。
相比于Prim的加点,Kruskal是通过加边实现最小生成树。即从所有边中选取权值最小的边,在满足加入条件后,将其的两个端点加入最小生成树。在被考察边的两个顶点是否位于两个联通分量(是否与生成树中的边形成回路)。如果如果将同一个连通分量的顶点放入一个集合中,则 Kruskal算法需要判断被考察边的两个顶点是否位于两个集合,以及将两个集合进行合并等操作。
Kruskal伪代码
算法: Kruskal算法
输入:无向连通网G=(V,E)
输出:最小生成树T=(U,TE)
1.初始化:U=V;TE={}
2.重复下述操作直到所有顶点位于一个连通分量
2.1在E中选取最短边(u,v);
2.2如果顶点u、v位于两个连通分量,则
2.2.1将边(u,v)并入TE;
2.2.2将这两个连通分量合成一个连通分量;
2.3在E中标记边(u,v),使得(u,v)不参加后续最短边的选取;
Kruskal算法存储结构
图的存储结构:因为 Kruskal算法依次对图中的边进行操作,因此考虑采用边集数组(edge set array)存储。为了提高查找最短边的速度,可以先对边集数组按边上的权值排序。
边集数组结构体定义:
struct
{
int from;
int to;
int weight;
}
连通分量的顶点所在的集合:由于涉及集合的查找和合并等操作,考虑采用并查集来实现。并查集是将集合中的元素组织成树的形式,合并两个集合,即将一个集合的根点作为另一个集合根结点的孩子。为了便于在并查集中进行查找和合并操作,树采用双亲表示法存储设数组parent[n],元素parent[i]表示顶点i的双亲(0≤i≤n-1)。初始时,令parent[i]=-1,表示顶点没有双亲,即每个集合只有一个元素。对于边(u,v),设vex1和vex2分别表示两个顶点所在集合的根,如果vex1≠vex2,则顶点和u和v一定位于两个集合,令 parent[vex2]=vex1,实现合并两个集合。
Kruskal实现最小生成树代码
#include<iostream>
using namespace std;
struct EdgeType //定义边集数组的元素类型
{
int from, to, weight; //假设权值为整数
};
const int MaxVertex = 10; //图中最多顶点数
const int MaxEdge = 100; //图中最多边数
template //定义模板类
class EdgeGraph
{
public:
EdgeGraph(DataType a[ ], int n, int e); //构造函数,生成n个顶点e条边的连通图
~EdgeGraph( ); //析构函数
void Kruskal( ); //Kruskal算法求最小生成树
private:
int FindRoot(int parent[ ], int v); //求顶点v所在集合的根
DataType vertex[MaxVertex]; //存储顶点的一维数组
EdgeType edge[MaxEdge]; //存储边的边集数组
int vertexNum, edgeNum;
};
template
EdgeGraph :: EdgeGraph(DataType a[ ], int n, int e)
{
int i, j, k, w;
vertexNum = n; edgeNum = e;
for (i = 0; i < vertexNum; i++)
vertex[i] = a[i];
for (k = 0; k < edgeNum; k++)
{
cout << "请输入边依附的两个顶点的编号,以及边上的权值:";
cin >> i >> j >> w; //输入边依附的两个顶点的编号
edge[k].from = i; edge[k].to = j; edge[k].weight = w;
}
}
template
EdgeGraph :: ~EdgeGraph( ){}
template
void EdgeGraph :: Kruskal( )
{
int num = 0, i, vex1, vex2;
int parent[vertexNum]; //双亲表示法存储并查集
for (i = 0; i < vertexNum; i++)
parent[i] = -1; //初始化n个连通分量
for (num = 0, i = 0; num < vertexNum - 1; i++) //依次考察最短边
{
vex1 = FindRoot(parent, edge[i].from);
vex2 = FindRoot(parent, edge[i].to);
if (vex1 != vex2) { //位于不同的集合
cout << "(" << edge[i].from << "," << edge[i].to << ")" << edge[i].weight << endl;
parent[vex2] = vex1; //合并集合
num++;
}
}
}
template
int EdgeGraph :: FindRoot(int parent[ ], int v) //求顶点v所在集合的根
{
int t = v;
while (parent[t] > -1) //求顶点t的双亲一直到根
t = parent[t];
return t;
}
int main( )
{
char ch[ ]={'A','B','C','D','E','F'};
EdgeGraph EG{ch, 6, 9};
EG.Kruskal( );
return 0;
}