Kruskal算法
一:思想
克鲁斯卡尔算法的具体思路是:将所有边按照权值的大小进行升序排序,然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边 组成回路,就可以作为最小生成树的一部分;反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。筛选出来的边和所有的顶点构成此连通网的最小生成树。
判断是否会产生回路的方法为:在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接。
假设遍历到一条由顶点 A 和 B 构成的边,而顶点 A 和顶点 B 标记不同,此时不仅需要将顶点 A 的标记更新为顶点 B 的标记,还需要更改所有和顶点 A 标记相同的顶点的标记,全部改为顶点 B 的标记。
例如,使用克鲁斯卡尔算法找上图的最小生成树的过程为:
首先,在初始状态下,对各顶点赋予不同的标记(用颜色区别),如下图所示:
对所有边按照权值的大小进行排序,按照从小到大的顺序进行判断,首先是(1,3),由于顶点 1 和顶点 3 标记不同,所以可以构成生成树的一部分,遍历所有顶点,将与顶点 3 标记相同的全部更改为顶点 1 的标记,如下图所示:
其次是(4,6)边,两顶点标记不同,所以可以构成生成树的一部分,更新所有顶点的标记为:
其次是(2,5)边,两顶点标记不同,可以构成生成树的一部分,更新所有顶点的标记为:
然后最小的是(3,6)边,两者标记不同,可以连接,遍历所有顶点,将与顶点 6 标记相同的所有顶点的标记更改为顶点 1 的标记:
继续选择权值最小的边,此时会发现,权值为 5 的边有 3 个,其中(1,4)和(3,4)各自两顶点的标记一样,如果连接会产生回路,所以舍去,而(2,3)标记不一样,可以选择,将所有与顶点 2 标记相同的顶点的标记全部改为同顶点 3 相同的标记:
当选取的边的数量相比与顶点的数量小 1 时,说明最小生成树已经生成。所以最终采用克鲁斯卡尔算法得到的最小生成树为上图所示。
二:实现代码
(一):结构
#define MAX_VERtEX_NUM 20 //定义最大顶点数量
#define VertexType int //定义顶点数据类型
typedef struct edge{
VertexType start; //起始点
VertexType end; //指向点
int weight; //起始点到终点上边的权值
}edge;
//定义辅助数组
typedef struct {
VertexType point; //顶点
int sign; //表示每个顶点所属的集合
}assist;
(二):算法
/**
* 克鲁斯卡尔最小生成树
* @param G
*/
void MinSpanTree_KRUSKAL(MGraph G){
assist assists[MAX_VERtEX_NUM];
edge edges[MAX_VERtEX_NUM];
InitData(G,edges,assists); //初始化数据
//对辅助数组进行排序
BubleSort(edges,G.arcnum);
//用来记录最小生成树中边的数量
int num = 0;
//遍历所有的边
for (int i = 0; i < G.arcnum; ++i) {
//找到当前权值最小的起点和结束顶点在assits数组中的位置
int start = LocateVex_ass(assists,edges[i].start,G.vexnum);
int end = LocateVex_ass(assists,edges[i].end,G.vexnum);
//如果start和end所表示的的点都存在
if (start!=-1 && end!=-1){
//开始的点与结束的顶点不属于同一个集合,则表明不会产生回路
if (assists[start].sign != assists[end].sign){
//输出,起点,终点,权值
printf("%d\t%d\t%d\n",edges[i].start,edges[i].end,edges[i].weight);
num++;
//将新加入到最小生成树中的顶点的标记全部改为相同的
for (int j = 0; j < G.vexnum; ++j) {
if (assists[j].sign == assists[end].sign ){
assists[j].sign = assists[start].sign;
}
}
//如果最小生成树中的边的数量比顶点数小1,则最小生成树已经生成
if (num == G.vexnum-1){
break;
}
}
}
}
}
//初始化辅助数组
void InitData(MGraph G,edge* edges,assist* assists){
//初始化辅助数组assits
for (int i = 0; i < G.vexnum; ++i) {
assists[i].point = G.vexs[i]; //顶点数据值
assists[i].sign = i; //初始化时每一个顶点的标志(所属集合)都是不一样的
}
//初始化edge数组
int count=0;
for (int i = 0; i < G.vexnum; ++i) {
for (int j = i+1; j < G.vexnum; ++j) {
if (G.arcs[i][j].adj!=INFINITY){
edges[count].start = G.vexs[i];
edges[count].end = G.vexs[j];
edges[count].weight = G.arcs[i][j].adj;
count++;
}
}
}
}
/**
* 在assit数组中查找顶点v,返回其在assit中的下标
* @param assists
* @param e
* @param length //数组长度
* @return
*/
int LocateVex_ass(assist* assists,VertexType v,int length){
for (int i = 0; i < length; ++i) {
if (assists[i].point == v){
return i;
}
}
return -1;
}
//排序
/**
* 将edge数组中的数据按照权值大小,从小到大进行排序
* @param edges
* @param numsize 数组长度
*/
void BubleSort(edge* edges,int numsize){
edge tump;
for (int i = 0; i < numsize; ++i) {
for (int j = numsize-1; j >i ; j--) {
if (edges[j].weight<edges[j-1].weight){
tump = edges[j];
edges[j] = edges[j-1];
edges[j-1] = tump;
}
}
}
}