最小生成树——克鲁斯卡尔算法(Kruskal算法)

克鲁斯卡尔算法(Kruskal算法)

对于n个顶点的连通图而言,其生成的最小生成树有n-1条边,即可以保证从任一点出发可以到达任一点且不产生回路。

克鲁斯卡尔算法(Kruskal算法):对每条边的权值进行从小到大排序,然后从小到大取权值最小的边,如取出的边会在树中产生回路则舍去,取下一条;若不会产生回路则加入到树中。
因此Kruskal算法的关键问题就是:如何判断新加入的边是否会产生回路

判断是否会产生回路的方法为:在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接,同时连接后会将其顶点标记改变。

克鲁斯卡尔算法(Kruskal算法)详细介绍

变化过程

以下图表示每个顶点标记状态的变化过程,两点颜色相同表示在同一边上,多点颜色相同表示在树上。
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第1张图片
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第2张图片
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第3张图片
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第4张图片
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第5张图片
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第6张图片
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第7张图片

实现方法:

建立两个结构体数组,一个用于存储边的信息edge edges,一个用于记录各个点的标记assist assists

typedef struct edge{
	VertexType initial; //起点
	VertexType end;//终点
	VertexType weight;//权值
}edge[MAX_VERtEX_NUM];
//定义辅助数组
typedef struct {
	VertexType value;//顶点数据
	int sign;//每个顶点所属的集合
}assist[MAX_VERtEX_NUM];

对edges进行升序排序,为方便观察用字母来表示起始点(实际存储的是int)。下面是edges(左)和 assists(右)的信息
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第8张图片
当AC构成一条边时,判断是否构成回路,因为AC的标记不同,所以成功。 信息C的标记就变为A的标记,同时AC这条边就相当于在树中存在了。下次就会从edges中的DF进行判断,更新两个数组的信息
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第9张图片
下次判断DF,成功,F的标记改为D的标记
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第10张图片
同样BE
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第11张图片
CF,判断成功,注意这时不仅需要把F变为C的标记,同时F后面连接的所有是树中的点都要改为C的标记,本例中FD都要改变
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第12张图片
AD标记相同,不成功,判断BC,成功。至此一共产生5条边,对于6个顶点的最少生成树,已经找出了满足其条件的5条边了。
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第13张图片
总结:对于新的树中的边,把终点及其所连接的所有是树中点的标记改为起始点的标记
如X——Y是新的边,x为起点,y为终点
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第14张图片
连接后各点标记会改为
最小生成树——克鲁斯卡尔算法(Kruskal算法)_第15张图片

int main(){

	int arcnum, vexnum;
	edge edges;//边信息的数组
	CreateUDN(&edges, &vexnum, &arcnum);
	//对连通网中的所有边进行升序排序,结果仍保存在edges数组中 使用的是库函数
	qsort(edges, arcnum, sizeof(edges[0]), cmp);
	//创建一个空的结构体数组,用于存放最小生成树
	edge minTree;
	//设置一个用于记录最小生成树中边的数量的常量
	int num = 0;
	//遍历所有的边
	for (int i = 0; i<arcnum; i++) {
		//找到边的起始顶点和结束顶点在数组assists中的位置
		int initial = Locatevex(vexnum, edges[i].initial);
		int end = Locatevex(vexnum, edges[i].end);
		//如果顶点位置存在且顶点的标记不同,说明不在一个集合中,不会产生回路
		if (initial != -1 && end != -1 && assists[initial].sign != assists[end].sign) {
			//记录该边,作为最小生成树的组成部分
			minTree[num] = edges[i];
			//计数+1
			num++;
			//将新加入生成树的顶点标记全部更改为一样的


			for (int k = 0; k<vexnum; k++) {//这一步是关键
				if (assists[k].sign == assists[end].sign) {
					assists[k].sign = assists[initial].sign;
				}
			}


			//如果选择的边的数量和顶点数相差1,证明最小生成树已经形成,退出循环
			if (num == vexnum - 1) {
				break;
			}
		}
	}
	//输出语句
	for (int i = 0; i<vexnum - 1; i++) {
		printf("%d,%d\n", minTree[i].initial, minTree[i].end);
	}
	return 0;
}

你可能感兴趣的:(算法,数据结构)