kruskal算法机试模板(C++)

我一直有点搞不清kruskal和prim的区别,但是kruskal就是简单粗暴的那个!就是一个图G,n个结点,我们对边进行排序,找到n-1条边就可以了(当然前提是这n-1条边不会连成环,如果连成环就换一个)。
前面的步骤都好说,主要是一个结构体Edge,让后对这个结构体的数组进行排序,然后就可以选择最小的。
主要是怎么判断有没有连城环,其实可以做一个已经连接的结点的vector,然后判断,如果这条边的起始和终止都已经属于了这个vector,说明再加这条边进去,就是会形成一个环了,这个时候就要机智的跳过,选择下一条边。
不过这个方法还是蠢了点,因为你得遍历寻找,会慢很多,然后再想想本质,其实可以看成,组成这一条边的两端的结点的集合的一个合并。因为这条边像是作为一个桥,把这些连接起来了,这个时候其实可以用到并查集,并查集就是判断连通分支数的,我们这个时候需要连通分支数>2,就是连接这条边的两个端点结点分别属于不同的集合,这样连接起来就不会有环出现,如果两个端点已经属于同一个连通分支,那相当于已经存在路,那怎么再加一条边呐,再加一条边就形成环了。
好了还有最后一个要点,那就是怎么判断是否能形成最小的生成树呐,其实得保证我们的图,是连通的,这样一定会有连通分支数,那这个可以在形成了并查集那个地方再判断一下。

#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#pragma warning(disable:4996);
//Kruskal算法,就是把Edge边进行排序,排序以后通过并查集进行选择
using namespace std;

struct Edge
{
	int from;
	int to;
	int val;
};
const int maxn = 100010;
vector<Edge> edges;
int Father[maxn];
int JudgeFather[maxn];
int visit[maxn] = { 0 };
//N个结点,M条无向边
int N, M;
int weight = 0;
bool cmp(Edge a, Edge b)
{
	return a.val < b.val;
}

void initFather(int n)
{
	for (int i = 0; i <= n; i++) {
		Father[i] = i;
		JudgeFather[i] = i;
	}
}

int Find(int x)
{
	if (x != Father[x]) {
		Father[x] = Find(Father[x]);
	}
	return Father[x];
}

void Union(int a, int b)
{
	a = Find(a);
	b = Find(b);
	Father[a] = b;
}

int main()
{
	scanf("%d %d", &N, &M); 
	initFather(N);
	if (M < N - 1)
	{
		printf("orz");
	}
	for (int i = 0; i < M; i++) {
		Edge e;
		scanf("%d %d %d", &e.from, &e.to, &e.val);
		edges.push_back(e);
	}
	sort(edges.begin(), edges.end(), cmp);
	int chooseEdge = 0;
	for (int i = 0; i < M; i++)
	{
		if (Find(edges[i].from) != Find(edges[i].to)) {
			Union(edges[i].from, edges[i].to);
			weight = weight + edges[i].val;
			chooseEdge++;
		}
	}
	if (chooseEdge == N-1) {
		printf("%d", weight);
	}
	else {
		printf("orz");
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kruskal算法是一种最小生成树算法,可以用来求无向图的最小生成树。下面是基于并查集实现的C++代码: ```c++ #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; int n, m; int f[MAXN], sz[MAXN]; // 并查集数组 struct Edge { int u, v, w; } e[MAXN]; bool cmp(Edge a, Edge b) { return a.w < b.w; } // 边按权值排序 int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); } // 并查集查找 void merge(int x, int y) { // 并查集合并 int fx = find(x), fy = find(y); if (fx != fy) { if (sz[fx] > sz[fy]) swap(fx, fy); f[fx] = fy; sz[fy] += sz[fx]; } } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) f[i] = i, sz[i] = 1; for (int i = 1; i <= m; i++) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w); sort(e + 1, e + m + 1, cmp); int cnt = 0, ans = 0; for (int i = 1; i <= m; i++) { if (cnt == n - 1) break; // 边数已经达到n-1条 if (find(e[i].u) == find(e[i].v)) continue; // 两个点已经在同一集合中 merge(e[i].u, e[i].v); ans += e[i].w; cnt++; } printf("%d\n", ans); return 0; } ``` 具体实现中,我们首先定义了一个结构体`Edge`,表示一条边,包括起点、终点和边权。然后定义了并查集数组`f`和`sz`,其中`f[i]`表示第i个节点所在的集合,`sz[i]`表示第i个集合的大小。接着,我们按照边权从小到大排序,依次遍历每条边,如果两个点已经在同一集合中,就跳过,否则将两个集合合并,同时累加边权。最后输出最小生成树的权值和即可。 注意,这段代码使用了路径压缩和按秩合并等优化,可以有效加速并查集的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值