• 并查集路径压缩


    并查集里的 find 函数里可以进行路径压缩,是为了更快速的查找一个点的根节点。对于一个集合树来说,它的根节点下面可以依附着许多的节点,因此,我们可以尝试在 find 的过程中,从底向上,如果此时访问的节点不是根节点的话,那么我们可以把这个节点尽量的往上挪一挪,减少数的层数,这个过程就叫做路径压缩。

    如下图中,find(4) 的过程就可以路径压缩,让数的层数更少。

    节点 4 往上寻找根节点时,压缩第一步,树的层数就减少了一层:

    节点 2 向上寻找,也不是根节点,那么把元素 2 指向原来父节点的父节点,操后后树的层数相应减少了一层,同时返回根节点 0。

    find 过程代码修改为 :

    1. // 查找过程, 查找元素p所对应的集合编号
    2. // O(h)复杂度, h为树的高度
    3. private int find(int p){
    4. assert( p >= 0 && p < count );
    5. // path compression 1
    6. while( p != parent[p] ){
    7. parent[p] = parent[parent[p]];
    8. p = parent[p];
    9. }
    10. return p;
    11. }

    上述路径压缩并不是最优的方式,我们可以把最初的树压缩成下图所示,层数是最少的。

    这个 find 过程代表表示为:

    1. ...
    2. // 查找过程, 查找元素p所对应的集合编号
    3. // O(h)复杂度, h为树的高度
    4. private int find(int p) {
    5. assert (p >= 0 && p < count);
    6. //第二种路径压缩算法
    7. if (p != parent[p])
    8. parent[p] = find(parent[p]);
    9. return parent[p];
    10. }
    11. ...

    Java 实例代码

    UnionFind3.java 文件代码:

    1. package runoob.union;
    2. /**
    3. * 基于rank的优化
    4. */
    5. public class UnionFind4 {
    6. private int[] rank; // rank[i]表示以i为根的集合所表示的树的层数
    7. private int[] parent; // parent[i]表示第i个元素所指向的父节点
    8. private int count; // 数据个数
    9. // 构造函数
    10. public UnionFind4(int count){
    11. rank = new 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. rank[i] = 1;
    18. }
    19. }
    20. // 查找过程, 查找元素p所对应的集合编号
    21. // O(h)复杂度, h为树的高度
    22. private int find(int p){
    23. assert( p >= 0 && p < count );
    24. // 不断去查询自己的父亲节点, 直到到达根节点
    25. // 根节点的特点: parent[p] == p
    26. while( p != parent[p] )
    27. p = parent[p];
    28. return p;
    29. //第二种路径压缩算法
    30. //if( p != parent[p] )
    31. //parent[p] = find( parent[p] );
    32. //return parent[p];
    33. }
    34. // 查看元素p和元素q是否所属一个集合
    35. // O(h)复杂度, h为树的高度
    36. public boolean isConnected( int p , int q ){
    37. return find(p) == find(q);
    38. }
    39. // 合并元素p和元素q所属的集合
    40. // O(h)复杂度, h为树的高度
    41. public void unionElements(int p, int q){
    42. int pRoot = find(p);
    43. int qRoot = find(q);
    44. if( pRoot == qRoot )
    45. return;
    46. if( rank[pRoot] < rank[qRoot] ){
    47. parent[pRoot] = qRoot;
    48. }
    49. else if( rank[qRoot] < rank[pRoot]){
    50. parent[qRoot] = pRoot;
    51. }
    52. else{ // rank[pRoot] == rank[qRoot]
    53. parent[pRoot] = qRoot;
    54. rank[qRoot] += 1; // 维护rank的值
    55. }
    56. }
    57. }

  • 相关阅读:
    入站一个月涨粉80万!B站竖屏UP主如何突出重围?
    二叉树非递归遍历
    如何批量为文件夹命名
    C和指针 第15章 输入/输出函数 15.13 改变缓冲方式
    java基础10题
    【归并】Leetcode 排序数组
    vue+flask微博大数据舆情监控+情感分析可视化系统+爬虫
    设计模式之观察者模式学习笔记
    一零一九、岗位数据分析(Spark)
    MQTT,如何在SpringBoot中使用MQTT实现消息的订阅和发布
  • 原文地址:https://blog.csdn.net/weixin_45953332/article/details/133849669