算法基础篇(9)------并查集

来源:互联网 发布:管家婆软件的优缺点 编辑:程序博客网 时间:2024/06/05 02:36

● 每周一言

见的事情越多,越容易眼高手低。

导语

前面七节已经把第一节提到的算法都讲完了。对于数据结构中的树和图,由于在实现这些算法的时候会随之用到,不单列出来细讲。不过在基础算法中,有不少在第一节中并未提及,所以再续上几节讲讲。

本节讲并查集。并查集虽然算一个偏冷门的算法,但效率还是很高的。那么,并查集的应用场景如何?算法又是如何实现的?

并查集

我们依然通过一个具体的例子切入:N个人,约定存在血缘关系即为亲人,且如果X和Y是亲人、Y和Z是亲人,则X和Z也是亲人。现给出M对人的血缘关系,保证这N个人都已涵盖在内。求这N个人中一共有多少个家族,并询问T次任意两人是否属于同一个家族。

fig0

根据题目描述,不难想到一种暴力枚举法,那就是每次将有血缘关系的两个人所属的家族集合重置为同一个值。可想而知当N、M、T较大时,算法时间复杂度将不可估量。此时,并查集横空出世。

并查集解法的最优数据结构是森林,算法思想是:N个人作为N棵树,首先初始化每棵树为一个家族;然后根据给出的M对血缘关系,合并子树组成新的家族。比如给出5组血缘关系:(a,b)(b,c)(d,e)(d,f)(d,b),并查集构建步骤如下图所示:

fig1

上图中字母后面的数字代表家族编号。首先(a,b)表示a和b有血缘关系,将b所在的树根指向a所在的树根组成一棵新的家族树;然后依次按照此规则更新(b,c)(d,e)(d,f)(d,b)这四组血缘关系。这里显示说明一下,当处理(d,b)的时候,由于b不是当前家族树的根节点,因此需要先找到根节点a,再把a指向d。此外,为了防止树退化成链表,通常将深度较小的树指到深度较大的树的根上,因此上图(6)的连法需更新成如下:
fig2

可知家族数量即为森林中最后形成的树数量。当查询任意两个人是否属于一个家族时,只需要分别比较这两个节点所在树的根节点家族即可。为了方便理解,上面的图直接把子树所有节点的家族编号都置为与根节点一致。

路径压缩 不难发现,当家族树某个分支过长的时候,每次合并或查找仍然会耗费不少时间,这时需要用一种叫路径压缩的方法来优化。一句话概括其优化思想为:每次找树根的时候将经过的所有节点均修改为指向根节点,把较长的路径变成多子树,路径压缩也因此得名。更新后的步骤如下图所示:

fig3

并查集的时间复杂度为O(MlogN),路径压缩后每次查询的时间复杂度为O(C)常数级别。敬请期待下节内容 匈牙利算法

结语

感谢各位的耐心阅读,后续文章于每周日奉上,欢迎大家关注小斗公众号 对半独白

face

0 0
原创粉丝点击