kruskal 最小生成树

Graph 图论

kruskal 最小生成树

相对于prim算法对于图中的点进行处理,kruskal则是对于边进行处理,我们在使用kruskal进行处理的时候,需要用到并查集的思想,后面我们会说。
kruskal算法实现的大致思路是先对边进行从小到大排序,如果这条边的起点和终点已经在一个集合了,说明加上这条边不会增加新的点对于当前这两个点所在的集合,如果这两个点属于同一个集合,那么把这条边连起来之后会让两个集合变成同一个集合(我们的目的不就是把所有点通过边归到一个集合里面吗),重复此操作直到加入集合的点达到|V|个,或者把|E|条边遍历完为止,如果遍历完所有的边不能使所有点归入到一个集合,那么这个图是不连通的。
借用洛谷的一张图:
在这里插入图片描述
根据上面的图我们可以知道我们在往图里面加边的时候是根据边的大小把图分成了不同的连通图,直到最后我们把所有的点都连在一起,这也是用了贪心的算法,每次都挑选最短的边,如果这条边不符合要求(所连的两个点本身就在同一个集合里)就不加,最后的结果肯定也是最短的。
判断两个点时候属于一个集合我们可以使用并查集的来实现,并查集是一个什么东西呢?并查集其实就是一个找祖先的过程,定义如果两个点的祖先相同,那么他们属于同一个集合。并查集只需要注意三个地方就可以了:
①数组的初始化:因为刚开始的时候每个点都不连通,所以每个点自己是一个集合,他们的共同祖先都是他们自己
for(int i=1;i<=n;i++){ f[i] = i; }
这一步千万不要漏掉!!!
②我们初始化完成之后我们就开始找祖先了,在这里我们用到一个数组f[maxn],用来存储节点 i 的父节点,这是什么意思呢?想一下你父亲有父亲,你父亲的父亲也有父亲,一直往上找,总归有一个祖先,这样一步步找父亲的过程就实现了找祖先的过程,那么怎样才算是找到呢?我们刚刚初始化的时候是不是把f[i] = i;,所以当他的父亲是他自己的时候我们的查找也算完成了。

int find(int n){//找节点为n的祖先
	while(n!=f[n]){
		n = f[n] = f[f[n]];//他爷爷和他父亲和他自己的祖先相同,把他爷爷的爸爸给他,节省后续查找时间,避免树的高度太高
	}
	return n;
}

③最后一步就是把两个集合归并到一起了,假设A集合的祖先是A,B集合的祖先是B,如果我们想把这两个集合归并到同一个集合,那么实际上也就是让他们的祖先相同,也就是让B的祖先变成A(反过来也可以),也就是f[B] = A;,这样就完成了两个集合的归并,因为B的子孙往上找找到B的时候还能继续找B的父亲节点,最终找到A,所以这样就实现了两个集合的归并(使两个集合有了相同的祖先)完整代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int sum = 0;
struct edge{
	int from,to;
	int cost;
};
bool cmp(edge x,edge y){
	return x.cost < y.cost;
}
edge map[500001];
int f[1000001];
int find(int n){
	while(n!=f[n]){
		n = f[n] = f[f[n]];
	}
	return n;
}
void kruskal(){
	int k = 1;
	int cnt = 0;
	while(k<n){
		edge e = map[cnt++];
		int a = find(e.from);
		int b = find(e.to);
		if(a==b){
			continue;
		}
		else{
			f[b] = a;
			k++;
			sum+=e.cost;
		}
	}
}
int main(){
	cin >> n >> m;
	for(int i=1;i<=n;i++){
		f[i] = i;
	}
	for(int i=0;i<m;i++){
		edge e;
		int a,b,c;
		cin >> a >> b >> c;
		e.from = a;
		e.to = b;
		e.cost = c;
		map[i] = e;
	}
	sort(map,map+m,cmp);
	kruskal();
	cout << sum << endl;
	return 0;
} 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Kruskal算法的C语言实现,用于求解无向加权图的最小生成树: ```c #include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 20 #define MAX_EDGE_NUM (MAX_VERTEX_NUM * (MAX_VERTEX_NUM - 1) / 2) typedef struct { int from, to; // 边的起点和终点 int weight; // 边的权重 } Edge; int parent[MAX_VERTEX_NUM]; // 并查集数组,用于判断是否形成环 // 初始化并查集 void InitSet(int n) { for (int i = 0; i < n; i++) { parent[i] = -1; } } // 查找元素所在集合的根节点 int FindSet(int x) { while (parent[x] >= 0) { x = parent[x]; } return x; } // 合并两个集合 void Union(int root1, int root2) { if (root1 == root2) { return; } if (parent[root1] > parent[root2]) { parent[root2] += parent[root1]; parent[root1] = root2; } else { parent[root1] += parent[root2]; parent[root2] = root1; } } // Kruskal最小生成树算法 void Kruskal(Edge* edges, int edgeNum, int vertexNum) { InitSet(vertexNum); // 初始化并查集 int count = 0; int i = 0; while (count < vertexNum - 1 && i < edgeNum) { // 边数不足n-1或者所有边都已考虑完毕则停止循环 int root1 = FindSet(edges[i].from); int root2 = FindSet(edges[i].to); if (root1 != root2) { printf("(%d, %d) %d\n", edges[i].from, edges[i].to, edges[i].weight); Union(root1, root2); count++; } i++; } } int main() { int vertexNum, edgeNum; printf("Please input the number of vertices and edges: "); scanf("%d %d", &vertexNum, &edgeNum); Edge edges[MAX_EDGE_NUM]; printf("Please input the edges and weights:\n"); for (int i = 0; i < edgeNum; i++) { scanf("%d %d %d", &edges[i].from, &edges[i].to, &edges[i].weight); } Kruskal(edges, edgeNum, vertexNum); return 0; } ``` 在该代码中,我们使用了并查集来判断是否形成环,并实现了Kruskal算法来求解最小生成树

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值