并查集(路径压缩法+启发式合并法)
我们从一道例题看起:洛谷P1551 亲戚。
问题很简单,给出一个亲戚关系图,规定
和 是亲戚, 和 是亲戚,那么 和 也是亲戚,那么 的亲戚都是 的亲戚, 的亲戚也都是 的亲戚,再给定 和 ,询问他们是否是亲戚,输出 Yes
或No
。人数,亲戚关系,询问次数
。
这样的亲戚关系图我们可以把它看作若干个集合,一个人就是一个元素,得知两个人是亲戚后,将他们各属于的集合合并即可,查询
因为一个元素只可能属于一个集合,所以我们可以为每一个集合选取一个代表元。这样子做,我们查询两个元素是否属于同一个集合就只需要比较他们各自的集合的代表元是否相同即可。
我们尝试实现合并集合和查询集合代表元这两个操作,查询集合代表元可以用数组标记将时间复杂度优化成
这道题我们就可以使用并查集来解决,它的思路是,维护一个树形结构,对于同一个集合中的元素,把他们之间的关系构成一棵树,那么这棵树就有相同的根节点,我可以通过判断他们根节点是否相同,来判断他们是否处于同一个集合中。
1|0初始化
由于初始时每一个集合都只有一个元素,所以把这些集合都单独看作一棵树。
以这道题的样例为例:
有
写成代码就是:
2|0合并
将两个集合合并,就是对两棵树合并,将其中一棵树的根结点变成另一棵树的根结点,如我们要合并
写成代码也很简单:
3|0查询
还是对于上面那几棵树,查询
合并和查询的 findfa 函数都使用路径压缩法,因为我们不管在合并还是查询操作,都只关心每棵树的根结点,我们就干脆不记录结点的父亲,只记录该结点所属的这棵树的根结点,并更新沿途经过结点的父亲为根结点即可,findfa 函数还有一种非递归形式,如下:
这样子,我们每次操作的时间复杂度只有
4|0启发式合并
上面那种路径压缩法,每一次合并,我们都会把一棵树的根设为另一棵树的根,这样就会把树摞得很高很高,因此,我们可以把两棵树中树高小的那棵树的根结点设为另一棵树的根,这样,只有树高一样时,合并后的树高才是他们其中的一个树高加
具体代码实现如下:
这样,相比于路径压缩法,启发式合并法的单次操作优化到
当我们将路径压缩法和启发式合并法放在一起,单次操作的时间复杂度只有
该题的路径压缩法代码如下:
__EOF__

本文链接:https://www.cnblogs.com/shimingxin1007/p/18363102.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架