算法学习笔记(30)——Kruskal算法(最小生成树)
Kruskal 算法
首先,将所有边按照权重从小到大排序。这一步是Kruskal算法的性能瓶颈,时间复杂度为 \(O(m \log m)\) 。不过由于排序的时间复杂度常数很小,所以Kruskal算法实际上是很快的。
第二部从小到大枚举每条边 \(a \rightarrow b\),边权为 \(w\) ,如果 \(a\) 和 \(b\) 目前不在一个连通块中,就把这条边加入最小生成树。该部分可以用并查集来实现。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10, M = 2e5 + 10;
struct Edge
{
int a, b, w;
// 运算符重载(常量成员函数)
bool operator< (const Edge &e) const {
return w < e.w;
}
}edges[M];
int n, m;
int p[N]; // 并查集
// 并查集查找操作(路径压缩优化)
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void kruskal()
{
// 首先对所有边按边权从小到大排序
sort(edges, edges + m);
// 初始化并查集
for (int i = 1; i <= n; i ++ ) p[i] = i;
// res存储最小生成树的边权之和,cnt存储最小生成树的边数
int res = 0, cnt = 0;
// 从小到大枚举每条边
for (int i = 0; i < m; i ++ ) {
auto e = edges[i];
int a = e.a, b = e.b, w = e.w;
// 如果a和b不在同一集合,则将这条边加入最小生成树
if (find(a) != find(b)) {
p[find(a)] = find(b);
res += w;
cnt ++;
}
}
// 根据最小生成树定义判断(n个点的最小生成树的边数一定为n-1)
if (cnt < n - 1) puts("impossible");
else cout << res << endl;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < m; i ++ ) {
int u, v, w;
cin >> u >> v >> w;
edges[i] = {u, v, w};
}
kruskal();
return 0;
}