克鲁斯卡尔算法解读

本文介绍了克鲁斯卡尔算法用于构造最小生成树的基本思想和步骤,详细讲解了如何通过局部贪心策略按边权重排序并利用并查集避免形成环。同时,文中提供了一个编程初学者编写的克鲁斯卡尔算法源代码,帮助读者理解算法的实现过程。
摘要由CSDN通过智能技术生成

克鲁斯卡尔算法–最小生成树

那个男孩不想写出自己的最小生成树,我叫AAA
通过这篇文章,你能看到:

  • 克鲁斯卡尔算法的源代码

  • 什么是最小生成树

  • 克鲁斯卡尔算法所涉及到的其他算法

克鲁斯卡尔算法的源代码
源代码如下:

#include <iostream>
#include <algorithm>
#define MAXNP 100010
#define MAXNE 100010
using namespace std;
int Q[MAXNP];
struct edge{
	int from,to,len;
}edges[MAXNE];
int cmp(edge a,edge b)
{
	return a.len < b.len;
}
int find(int a)
{
	int root = a;
	while(root != Q[root])
	{
		root = Q[root];
	}
	while(root != a)
	{
		int tmp = Q[a];
		Q[a] = root;
		a = tmp;
	}
	return root;
}
int main() 
{
	int n,m;
	cin>>n>>m; 
	for(int i = 1;i <= n;i++)
	{
		Q[i] = i;
	}
	for(int i = 1;i <= m;i++)
	{
		cin>>edges[i].from>>edges[i].to>>edges[i].len;
	}
	sort(edges+1,edges+1+n,cmp);
	int cnt = 0,ans = 0;
	for(int i = 1;i <= n,cnt < n - 1;i++)
	{
		int tmpto = find(edges[i].to);
		int tmpfrom = find(edges[i].from);
		if(tmpto != tmpfrom)
		{
			cnt++;
			Q[tmpto] = tmpfrom;
			ans += edges[i].len;
		}
	}
	cout<<ans;
	return 0;
}

这是一个编程初学者(我)写的代码,仅供参考。

什么是最小生成树

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n
个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。 --度娘

用我的理解来说:最小生成树就是把一个图砍掉一些边,变成边权重和最小的树。
那么,这里的边应该怎么砍?如何才能让砍掉的边是树中不需要的边?
这就是我们需要了解的了。

这里的边应该怎么砍?

我们可以通过局部贪心的算法目前来实现一味的砍掉最小边。
首先 将输入的边按照权值从小到大进行排序:
在边的结构体中,有三个数据:来的节点,去的节点,以及边的长度

struct edge{
	int from,to,len;
}edges[MAXNE];

其中的节点都是在一维int数组Q中进行存储

int Q[MAXNP];

我们只需要写一个给快速排序用的函数,就可以进行排序了

int cmp(edge a,edge b)
{
	return a.len < b.len;
}

快速排序不要忘记参数和头文件

sort(edges+1,edges+1+n,cmp);

但是这样一味砍掉最小的边有可能将树上有用的边砍掉
所以我们探讨到了第二个问题

如何才能让砍掉的边是树中不需要的边?

这里我们就需要了解一个至关重要的东西

并查集

我们来假设这样一个情景:
A和B是亲戚关系,B和C是亲戚关系,D和E是亲戚关系
如果他们互相了解彼此之间的关系的话,不出意外的情况下
A和C见面会互相寒暄,A和E不会
因为A和C都有一个亲戚是B,所以他们之间是亲戚关系
这就是一个小型的并查集问题
并查集问题的核心就是将一条链上的点都指向一个根节点,我们称之为

路径压缩

对于我们的问题,我们将插入的边的两个点放到集合里,然后将每个可能被插入的边的两个节点分别进行在集合中的搜索,如果搜索的结果是这两个节点有同一个根节点,我们就知道这两个集合已经连接在一起了,(因为我们已经是将边权值从小到大排序了,所以先前的边已经放进了树里,所以这里的边就不需要了)我们就可以对他说
“你的简历我已经看了,有没有被录用回家等通知吧”
然后依次遍历每条边,加上输入输出,我们的克鲁斯卡尔算法就完成了

并查集算法

int find(int a)
{
	int root = a;
	while(root != Q[root])
	{
		root = Q[root];
	}
	//找到根节点
	while(root != a)
	{
		int tmp = Q[a];
		Q[a] = root;
		a = tmp;
	}
	//将此线上的节点指向的节点都指向根节点
	return root;
}

关键语句

	for(int i = 1;i <= n,cnt < n - 1;i++)
	{
		int tmpto = find(edges[i].to);//看看来的点的根节点
		int tmpfrom = find(edges[i].from);//看看去的点的根节点
		if(tmpto != tmpfrom)
		{
			cnt++;
			Q[tmpto] = tmpfrom;//使用Q[a] = b代表a和b有联系
			ans += edges[i].len;
		}
	}

结语

感谢你能看到这里,我是一个初二的学生,这是我第一次写博客。代码可能会出现一些问题。如果有问题,请多指教

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值