今天学习了并查集,主要思想是把每一个集合用一个树来表示,选出一个代表。这样可以做到快速求并集与查询任意两个元素是否在同一个集合之内。为了让查询代表的速度更快,常常需要路径压缩;但这样可能会丢失信息,有时也会使用启发式合并。
注:目前没学过的并查集相关内容:可持久化并查集,权值并查集
1. 普通并查集(带路径压缩)
const int maxn = 10050;
int fa[maxn]
void makeset(int n){
for (int x = 0; x < n; ++x) fa[x] = x;
}
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
if(x != y) fa[x] = y;
}
2.启发式合并
这里我见过两种写法,但都有路径压缩,没太搞懂路径压缩与启发式合并这两者的关系
//id[i]存编号为i的集合,fa[i]表示i元素所在集合的代表
const int maxn = 10050;
vector<int> id[maxn];
int fa[maxn],n,q;
inline void makeset(int size){
ms0(id);
for(int i = 0; i < size; ++i) fa[i] = i, id[i].push_back(i);
}
inline int find(int x){
return fa[x];
}
inline void Union(int x,int y){
x = fa[x], y = fa[y];
if(x == y) return;
if(id[x].size() > id[y].size()) swap(x, y);
for(int i = id[x].size() - 1; i >= 0; --i){
int cur = id[x][i];
fa[cur] = y;
id[y].push_back(cur);
}
id[x].clear();
}
//f[]数组存放根节点,rank[]数组来存放根节点的深度,num[]数组来存放节点个数,rank[]数组和num[]数组的初始化都应为1
//启发式合并:
void Union(int x,int y)
{
f_X=find(x);
f_y=find(y);
if(f_x==f_y) return;
if(rank[f_x]>rank[f_y])
{
f[f_y]=f_x;
num[f_x]+=num[f_y];
}
else
{
f[f_x]=f_y;
num[f_y]+=num[f_x];
if(rank[f_x]==rank[f_y])
{
rank[f_y]++;
}
}
}
//路径压缩:
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}