学习笔记 - 并查集

本人实力不济,如有错误或建议及补充,请指出

1.定义

并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素。
顾名思义,并查集支持两种操作:

  • 合并(Union):合并两个元素所属集合(合并对应的树)
  • 查询(Find):查询某个元素所属集合(查询对应的树的根节点),这可以用于判断两个元素是否属于同一集合

并查集在经过修改后可以支持单个元素的删除、移动。

(注:这两段文字均源于OI wiki

2.实现

我们首先设置一个数组fa代表i的父亲是fa[i]

2.0 初始化

我们假设每个节点的父亲刚开始都是自己,即:

int fa[N];
void init(){
for(int i=1;i<=n;i++) fa[i]=i;
}

2.1 查询自己的祖先(find函数的实现)

2.1.1 朴素实现

顾名思义,X先查询自己的父亲Y,Y再查询自己的父亲是Z……直到最高的一辈,即F查询到自己的父亲是F。
此时F就是X的祖先。

int find(int x)
{
	if(fa[x]!=x) return find(fa[x]);//不是祖先,找它的父亲
	else return x;//找到了祖先,返回
}

2.1.2 路径压缩

但考虑极端情况,朴素算法的并查集构成的树是一条链式的,时间复杂度是O(n)。

再回归定义,我们只需要该树的根节点就够了,所以此时可以考虑路径压缩

我们可以把每个结点直接连接到根节点。

代码实现:

int find(int x)
{
	if(fa[x]!=x) return fa[x]=find(fa[x]);//去找祖先,将找到的祖先设为自己的父亲(辈分乱了啊喂)
	else return x;
}

除了第一次查询时间复杂度为O(n),后续几次查询均为O(1)

(谁家好人费怎么大劲造个并查集结果只查询一次)

2.2 合并集合(Union函数)

为什么不将函数名取为union呢,因为union是C++中的联合体(一种关键字就是了)。

由于路径压缩已经介绍了,这里就不提供朴素实现了

void find(int x,int y)
{
	int fax=find(x),fay=find(y);//查询X,Y的祖先
	fa[fay]=fax;//将Y的祖先的父亲设为X的祖先
}

3.拓展阅读

posted @ 2024-10-20 20:47  AC-13-13  阅读(4)  评论(0编辑  收藏  举报