图结构专栏——最小生成树之Kruskal算法

图结构专栏——最小生成树之Kruskal算法

一、最小生成树

在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树。

例如,对于上图所示的连通网可以有多棵权值总和不同的生成树,而最后的生成树是最小生成树。

在这里插入图片描述

二、Kruskul算法

克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。

  • 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路。
  • 具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止。

三、Kruskul图解

以上图为例,对Kruskul进行演示

第一步,取边<0,2>

在这里插入图片描述

第二步,取边<3,5>

在这里插入图片描述

第三步,取边<1,4>

在这里插入图片描述

第四步,取边<2,5>

在这里插入图片描述

第五步,取边<1,2>

在这里插入图片描述

四、如何判断回路

在kruskal算法中有一步很重要的是判断加入这条边会不会产生回路,在这里我们使用的是并查集来帮助判断会不会产生环,关于并查集的详细知识可以查看这篇博客进行学习《并查集以及路径压缩》,这里我们说一说到底怎么样使用并查集判断是否有环。

在这里插入图片描述
对于上面这个图,我们使用并查集依次先加入<1,2>,<2,3>和<1,3>

第一步,初始化parents
i123
parents[i]123
第二步,加入边<1,2>,令节点2当父节点
i123
parents[i]223
第三步,加入边<2,3>,令节点3当父节点
i123
parents[i]233
第四步,加入边<1,3>

当需要加入这条边时,发现节点1和节点3有共同的祖先3,这个时候就判断出来如果加入这条边的话图会出现环

五、代码实现

vector<int> parents;//并查集,存储每个节点的父节点/祖先节点
vector<vector<int>> edges;//记录边的信息,每个vector存放一条边的信息,分别是头节点、尾节点和这条边的权值
//nodenum是总结点数
void initialparent(int nodenum)
{
	parents.resize(nodenum, -1);
	for (int i = 0; i < nodenum; i++)
	{
		parents[i] = i;
	}
}

//寻找node节点的最祖先的节点,同时对该节点的所有祖先做路径压缩
int find(int node)
{
	//寻找最祖先的节点
	int r = node;
	while (parents[r] != r)
	{
		r = parents[r];
	}
	int t = node;
	//压缩路径
	while (t != r)//t是从该节点node开始向上到达祖先节点r的所有节点
	{
		int temp = parents[t];
		parents[t] = r;
		t = temp;
	}
	return r;
}

//对边进行排序,按权值大小从大到小的顺序
void swapEdges()
{
	for (int i = 0; i < edges.size(); i++)
	{
		for (int j = i; j < edges.size(); j++)
		{
			if (edges[i][2] > edges[j][2])
			{
				vector<int> t = edges[i];
				edges[i] = edges[j];
				edges[j] = t;
			}
		}
	}
}

void merge(int x,int y)
{
	int r1 = find(x);
	int r2 = find(y);
	if (r1 == r2)
		return;
	if (parents[r1] > parents[r2]) {
		parents[r1] = r2;
	}
	else {
		parents[r2] = r1;
	}
}


//存储图的结构,使用邻接表存储,graph[i]存储与节点i相连的节点
void kruskal(int num)
{
	int sumWeight = 0;//权重之和
	int u, v;//一条边的头节点和尾节点
	initialparent(num);//初始化并查集数组
	swapEdges();
	for (int i = 0; i < edges.size(); i++)
	{
		u = edges[i][0];
		v = edges[i][1];

		if (find(u) != find(v)) { //u和v不在一个集合
			printf_s("加入边:%d %d,权值: %d\n", u, v, edges[i][2]);
			sumWeight += edges[i][2];
			merge(u, v); //把这两个边加入一个集合。
		}
	}
	printf("weight of MST is %d \n", sumWeight);
}

六、结果展示

在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值