• 并查集快速合并


    对于一组数据,并查集主要支持两个动作:

    • union(p,q) - 将 p 和 q 两个元素连接起来。

    • find(p) - 查询 p 元素在哪个集合中。

    • isConnected(p,q) - 查看 p 和 q 两个元素是否相连接在一起。

    在上一小节中,我们用 id 数组的形式表示并查集,实际操作过程中查找的时间复杂度为 O(1),但连接效率并不高。

    本小节,我们将用另外一种方式实现并查集。把每一个元素,看做是一个节点并且指向自己的父节点,根节点指向自己。如下图所示,节点 3 指向节点 2,代表 3 和 2 是连接在一起的,节点2本身是根节点,所以指向自己。

    同样用数组表示并查集,但是下面一组元素用 parent 表示当前元素指向的父节点,每个元素指向自己,都是独立的。

    如果此时操作 union(4,3),将元素 4 指向元素 3:

    数组也进行相应改变:

    判断两个元素是否连接,只需要判断根节点是否相同即可。

    如下图,节点 4 和节点 9 的根节点都是 8,所以它们是相连的。

    连接两个元素,只需要找到它们对应的根节点,使根节点相连,那它们就是相连的节点。

    假设要使上图中的 6 和 4 相连,只需要把 6 的根节点 5 指向 4 的根节点 8 即可。

    构建这种指向父节点的树形结构, 使用一个数组构建一棵指向父节点的树,parent[i] 表示 i 元素所指向的父节点。

    1. ...
    2. private int[] parent;
    3. private int count; // 数据个数
    4. ...

    查找过程, 查找元素 p 所对应的集合编号,不断去查询自己的父亲节点, 直到到达根节点,根节点的特点 parent[p] == p,O(h) 复杂度, h 为树的高度。

    1. ...
    2. private int find(int p){
    3. assert( p >= 0 && p < count );
    4. while( p != parent[p] )
    5. p = parent[p];
    6. return p;
    7. }
    8. ...

    合并元素 p 和元素 q 所属的集合,分别查询两个元素的根节点,使其中一个根节点指向另外一个根节点,两个集合就合并了。这个操作是 O(h) 的时间复杂度,h 为树的高度。

    1. public void unionElements(int p, int q){
    2. int pRoot = find(p);
    3. int qRoot = find(q);
    4. if( pRoot == qRoot )
    5. return;
    6. parent[pRoot] = qRoot;
    7. }

    Java 实例代码

    UnionFind2.java 文件代码:

    1. package runoob.union;
    2. /**
    3. * 第二版unionFind
    4. */
    5. public class UnionFind2 {
    6. // 我们的第二版Union-Find, 使用一个数组构建一棵指向父节点的树
    7. // parent[i]表示第一个元素所指向的父节点
    8. private int[] parent;
    9. private int count; // 数据个数
    10. // 构造函数
    11. public UnionFind2(int count){
    12. parent = new int[count];
    13. this.count = count;
    14. // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
    15. for( int i = 0 ; i < count ; i ++ )
    16. parent[i] = i;
    17. }
    18. // 查找过程, 查找元素p所对应的集合编号
    19. // O(h)复杂度, h为树的高度
    20. private int find(int p){
    21. assert( p >= 0 && p < count );
    22. // 不断去查询自己的父亲节点, 直到到达根节点
    23. // 根节点的特点: parent[p] == p
    24. while( p != parent[p] )
    25. p = parent[p];
    26. return p;
    27. }
    28. // 查看元素p和元素q是否所属一个集合
    29. // O(h)复杂度, h为树的高度
    30. public boolean isConnected( int p , int q ){
    31. return find(p) == find(q);
    32. }
    33. // 合并元素p和元素q所属的集合
    34. // O(h)复杂度, h为树的高度
    35. public void unionElements(int p, int q){
    36. int pRoot = find(p);
    37. int qRoot = find(q);
    38. if( pRoot == qRoot )
    39. return;
    40. parent[pRoot] = qRoot;
    41. }
    42. }
  • 相关阅读:
    idea中使用git【图文详解】
    clickhouse数据结构和常用数据操作
    新人必看!手把手教你如何使用浏览器表格插件(上)
    使用css的transition属性实现抽屉功能
    Linux-网络配置、管理与基本应用
    FFplay文档解读-49-多媒体过滤器三
    【SA8295P 源码分析】111 - 使用 Infineon 工具升级DHU 的MCU 固件过程指导
    CDN缓存的原理是什么?CDN网络资源获取过程
    Docker使用问题汇总
    做自媒体视频二次剪辑,怎样剪辑不算侵权
  • 原文地址:https://blog.csdn.net/weixin_45953332/article/details/132793671