定义
并查集是一种树形的数据结构,用于处理不相交集合的查询和合并问题
有两种基本操作,查询(find)和合并(merge)
这里我们用一个通俗的例子
火并过程中4和2碰见了,为了确定是不是友军,4向3询问上级,3向1询问上级,发现1是boss,2向1询问上级,发现1是boss,此时2和4有了相同的boss,2和4是一个帮派的,那么就不会发生冲突
帮派1被帮派2打败了,帮派2要合并两个帮派,也就是说1的上级变成了6,此时两个帮派合二为一
find函数
现在我们来讲代码实现
直接实现:维护树上每个结点,若该节点是根节点则父亲视为自己
int fa[maxn];
int find(int x)
{
if(fa[x]==x)return x;
else return find(fa[x]);
}
但是这样find一次最坏为O(n)有没有更优化的办法呢
还是以上面的例子为例
这样就大大减少了树的深度
简单来说,就是将x到根节点路径上所有的点的上级都设置为根节点
代码如下
int newfind(int x)
{
if (fa[x] == x)return x;
return fa[x] = find(fa[x]);
}
merge函数
void merge(int x, int y)
{
if (find(x) == find(y))return;
fa[find(x)] = find(y);
}
让我们再来想想优化的事情
这里给出两种思路
1.根据树的高度优化
如果有两棵树,我们将高度较高的树的根节点作为代表元(boss),能优化使合成树的高度最小,实现优化
void newmerge1(int a, int b)//通过树的高度优化
{
if (find(a) == find(a)) return;
if (rank[a] > rank[b]) fa[b] = a; //如果 a的高度大于 b,则令 b的上级为 a
else //否则
{
if (rank[a] == rank[b]) rank[b]++; //如果 x的高度和 y的高度相同,则令 y的高度加1
fa[a] = b; //让 x的上级为 y
}
2.根据子树的结点数来优化
如果有两棵树,我们将结点少的树并到结点多的树上会使所形成的树的形态更优
void newmerge2(int a, int b)//通过结点个数优化
{
if (find(a) == find(b))return;
if (sz[a] > sz[b])swap(a, b);
fa[a] = b; sz[b] = sz[b] + sz[a];
}
模板题
洛谷P3367
代码:
#include<iostream>
using namespace std;
int n,m;
int f[10010];
int p1,p2,p3;
int find(int x)
{
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
f[i]=i;
}
while(m--)
{
cin>>p1>>p2>>p3;
if(p1==1)
{
f[find(p2)]=find(p3);
}
else if(find(p2)==find(p3))cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
return 0;
}