Kruskal算法
Kruskal算法介绍
- 简介: Kruskal算法是由克鲁斯卡尔提出的,是用来求加权连通图的最小生成树的算法
- 时间复杂度: O(mlogm)
- 空间复杂度:O(m)
- 使用范围: 稀疏图(朴素版本的Prim算法的时间复杂度为O(n2), 堆优化版本的Prim算法时间复杂度为O(mlogn),稀疏图问题就用Kruskal算法,稠密图问题就用朴素版的Prim算法,堆优化版本的Prim算法没有Kruskal算法好并且比较难写)。无向图(有向图一般用不到)
- 核心思想: 贪心,按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路.
- 算法步骤:
1.按照边权重从小到大排序(有自环与重边没事)
2.从小到大遍历所有边,如果边上两点不在一个集合内,则选择这条边,并将两点合并成一个集合
3.通过判断边的条数来判断是否存在最小生成树
Kruskal算法核心代码
//这里只需要用结构体存边权关系即可,记得重载"<"运算符方便sort操作,当然还可以手写cmp替代这方法
struct Edge{
int a, b, w;
bool operator < (const Edge t) const {return w < t.w;}
}edges[maxn2];
//p[]表示集合,n表示点数,m表示边数
int p[maxn1], n, m;
//并查集初始化操作
void init(int n) {
for(int i = 1; i <= n; ++i) p[i] = i;
}
//并查集查找操作
int find(int x) {
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int kruskal() {
sort(edges, edges + m);
int cnt = 0, res = 0; //一定记得初始化,cnt记录集合中边的数量
for(int i = 0; i < m; ++i) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
if(find(a) != find(b)) {
p[find(a)] = find(b);
cnt++;
res += w;
}
}
//因为这里用到并查集所以边数最多为n-1这时候集合有n个点,若是循环还没有停止,也不会进入if里面的语句了,n个点在同一个集合内了
if(cnt < n - 1) return INF;
return res;
}
Kruskal算法题目
#include <bits/stdc++.h>
using namespace std;
const int maxn1 = 1e5 + 10;
const int maxn2 = 2e5 + 10;
const int INF = 0x3f3f3f3f;
//这里只需要用结构体存边权关系即可,记得重载"<"运算符方便sort操作,当然还可以手写cmp替代这方法
struct Edge{
int a, b, w;
bool operator < (const Edge t) const {return w < t.w;}
}edges[maxn2];
//p[]表示集合,n表示点数,m表示边数
int p[maxn1], n, m;
//并查集初始化操作
void init(int n) {
for(int i = 1; i <= n; ++i) p[i] = i;
}
//并查集查找操作
int find(int x) {
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int kruskal() {
sort(edges, edges + m);
int cnt = 0, res = 0; //一定记得初始化,cnt记录集合中边的数量
for(int i = 0; i < m; ++i) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
if(find(a) != find(b)) {
p[find(a)] = find(b);
cnt++;
res += w;
}
}
//因为这里用到并查集所以边数最多为n-1这时候集合有n个点,若是循环还没有停止,也不会进入if里面的语句了,n个点在同一个集合内了
if(cnt < n - 1) return INF;
return res;
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
init(n);
for (int i = 0; i < m; ++i) {
int a, b, w;
cin >> a >> b >> w;
edges[i] = {a,b,w};
}
int res = kruskal();
if(res == INF) puts("impossible");
else cout << res << endl;
return 0;
}
Q&A
- Q1:不需要处理重边与自环吗?
不需要处理,因为会对结构体按边权从小到大排序所以,所以算法会选择最小的边加入到集合中;自环也不需要处理,因为有并查集,所以自环不可能进入if语句中