对并查集的一个直观理解
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png

    1. 并查集初始化 ```java int N = 100; // N 为初始集合的个数 int[] parent = new int[N];

    for(int i = 0; i < N; i++){ parent[i] = i;
    }

    1. 2. find 函数
    2. ```java
    3. // 最原始版本
    4. int find(int x) {
    5. if(parent[x] == x){ // 找到根节点了(类似于上面例子的帮派大佬)
    6. return x;
    7. }
    8. else {
    9. return find(parent[x]);
    10. }
    11. }
    12. // 路径压缩
    13. // 在递归找根节点的过程中,将这一过程中遍历到的非根结点的父节点直接指向为根结点
    14. // 使得每个元素到根节点的路径尽可能的短
    15. int find(int x){
    16. if(parent[x] == x){
    17. return x;
    18. }else{
    19. parent[x] = find(parent[x]);
    20. return parent[x];
    21. }
    22. }
    23. // 上述代码还可以进行简写
    24. int find(int x) {
    25. return parent[x] == x ? x : (parent[x] = find(parent[x]));
    26. }
    1. union 函数 ```java // 最原始版本 void merge(int i, int j) { parent[find(i)] = find(j); }

    // 按秩合并(秩可以理解为根节点的深度,对于非根节点而言,秩可能并没有表示深度) // 我们需要在初始化时,对rank[] 进行初始化 void init(int n){ for(int i = 0; i < n; i++){ //省略对parent的初始化

        rank[i] = i;
    }
    

    }

    void merge(int i, int j) { int x = find(i); int y = find(j);

    // 秩小的合并到秩大的树中
    // 此处为什么不先判断 x,y 是否相等?
    // 因为如果 x,y相等,下面的if-else 代码块仍然不会改变二者的parent
    if(rank[x] <= rank[y]){
        parent[x] = y;
    }else {
        parent[y] = x;
    }
    
    if(rank[x] == rank[y] && x != y){
        rank[y]++;   
    }
    

    }

    ```