小谈并查集
并查集是什么:
- “并”和“查”是两个功能;“集”表示集合
- 解决一种把大量数据分块后查找某些数据是否是同一类的问题。
- 一般由find和join函数组成,这里咱们就详细讲一下课本P169kruscal算法里面的并查集(标号法)
Kruscal最小生成树算法:
思路:
- 先将所有边从小到大排序
- 遍历所有边依次选出不在同一集合的最小边加入最小生成树
- Kruskal证明
注: 判断是否在同一集合就用 “并查集”.
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, m, maxv = 0, ans;//n:点的个数;m:边的条数;maxv:最大点;ans:最小生成树的值
int parent[N];// 每个点所属的集合
struct Edge//存每条边的信息
{
int head;//边的起点
int tail;//边的终点
int w;//边的权值
}edge[N];
bool cmp(Edge a, Edge b)//排序规则:按照权值w从小到大排序
{
return a.w < b.w;
}
void Kruscal()
{
sort(edge, edge + m, cmp);//排序
for(int i = 1; i <= maxv; i ++) parent[i] = i;//开始每个点的父亲初始为自己
for(int i = 0; i < m; i ++)//遍历排序后的每条边
{
int pos1 = edge[i].head;// pos1:边的始点
int pos2 = edge[i].tail;// pos2:边的终点
if(parent[pos1] != parent[pos2])// 当pos1与 pos2不在同一个集合时
{
ans += edge[i].w;//将该条边加入树中
printf("加入%d到%d之间权值为%d的边!\n", edge[i].head , edge[i].tail, edge[i].w);
//将大的标号更新为小的标号
int maxx = max(parent[pos1], parent[pos2]), minn = min(parent[pos1], parent[pos2]);
for(int j = 1; j <= maxv; j ++)
if(parent[j] == maxx) parent[j] = minn;
puts("此时个点的标号变为:");
for(int j = 1; j <= maxv; j ++)
printf("%d的标号为:%d\n", j, parent[j]);
}
}
printf("最小生成树权值为:%d\n", ans);
}
int main()
{
cin >> n >> m;
for(int i = 0; i < m; i ++)
{
int u, v, w;
maxv = max(maxv,max(u, v));
cin >> u >> v >> w;
edge[i] = {u, v, w};
}
Kruscal();
return 0;
}
/*
测试数据:
6 10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6
*/