并查集入门及例题分析

1、并查集的原理

并查集(Union-Find)是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。
主要涉及两种操做:合并和查找。 具体地说,初始状态下,并查集中的元素是互不相交的,通过一系列操做(Union)后,合并成一个集合。 而在进行了某次合并以后,若是想知道:某两个元素是否已经处在同一个集合中了?这时候就须要查找(Find)操做。
一张江湖解说图以下:
数组

简单来说,这个过程能够理解为找祖宗和认祖宗的故事。若是此时你想找到杨左使的管理者,就能够用并查集结构,若是你想让宋远桥归属到张无忌门派,就可使用到并查集结构。
那并查集的初始与结构是怎么样的呢?数据结构

2、并查集的数据结构及实现方式

一、构建并初始化并查集

初始化并查集结构,用上面图来讲,就是先初始有多少个江湖人士,而后默认他们都是各自一派的管理员。代码以下:优化

class findUnionCollection{
    private int[] s; // 江湖人数的数组集
    int count; // 门派个数
 
 
// 构建江湖人士的数组集和门派个数
    public findUnionCollection(int length){
        s = new int[length];
        s = initCollection(s);
        count = length;
    }
 // 将每一个江湖人士设置为各自一派的管理者(根节点默认为-1)
    private int[] initCollection(int[] target){
        for (int i =0;i<target.length;i++){
            target[i]=-1;
        }
        return target;
    }
}
复制代码

有了各个门派的一个集合,此时咱们应该也要有个能够找到每一个门派的管理者一个查找方法。spa

代码讲解:code

public int find(int x){
 
 // 当这个成员的所对应的值小于0,即为-1,则为门派的管理员,直接返回
        if(s[x]<0){
            return x;
        }
  // 若是不为0,则这个成员所对应的值是他的直接上层大佬
  // 此时能够去递归这个大佬的大佬,直到找到最终的管理员
        return find(s[x]);
    }
复制代码

固然,这里有个优化点,就是咱们这边只是关心这个门派的管理员,而不关心这个门派的各个层次的关系。因此咱们在查询的时候,能够将这层直接指引到管理员,方便下次直接查找,术语简称路径压缩。
代码以下:cdn

public int find(int x){
 
 // 当这个成员的所对应的值小于0,即为-1,则为门派的管理员,直接返回
        if(s[x]<0){
            return x;
        }
  // 若是不为0,则这个成员所对应的值是他的直接上层大佬
  // 此时能够去递归这个大佬的大佬,直到找到最终的管理员
      // 将各个门派的成员都指向最终管理员(实际就是路径压缩)
        return s[x] = find(s[x]);
    }
复制代码

有了找祖宗的方法,固然也少不了认祖宗的方法,换江湖的说话,就是投靠另外一门派blog

代码解析:递归

// 合并两个江湖人士所属门派
public void unionCollection(int child1,int child2){
 
// 先判断两个江湖人士是否所属一派,若是是,直接返回,无须合并
        if(find(child1)==find(child2)){
            return;
        }
// 以后咱们这个成员所属门派的管理员的深度进行对比
// 若是两个管理者的深度同样,就随机选一个管理员做为最终管理员,将加深其深度(减1)
// 若是child1的深度比child2的深度深,则将child2的管理员指向child1管理员
          if(s[find(child1)]<s[find(child2)]){
            s[find(child2)]=find(child1);
        }else {
            if(s[find(child1)]==s[find(child2)]){
                s[find(child2)]--;
            }
            s[find(child1)]=find(child2);
        }
因为每一次合并,都会减小一个门派,因此门派的数量也就递减
        count--;
    }
复制代码

3、例题实战

一、题目描述

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具备是传递性。若是已知 A 是 B 的朋友,B 是 C 的朋友,那么咱们能够认为 A 也是 C 的朋友。所谓的朋友圈,是指全部朋友的集合。get

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。若是Mi = 1,表示已知第 i 个和 j 个学生互为朋友关系,不然为不知道。你必须输出全部学生中的已知的朋友圈总数。it

示例 1:

输入:

[[1,1,0],

[1,1,0],

[0,0,1]]

输出: 2

说明:已知学生0和学生1互为朋友,他们在一个朋友圈。

第2个学生本身在一个朋友圈。因此返回2。

示例 2:

输入:

[[1,1,0],

[1,1,1],

[0,1,1]]

输出: 1

说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,因此学生0和学生2也是朋友,因此他们三个在一个朋友圈,返回1。

注意:

N 在[1,200]的范围内。

对于全部学生,有Mi = 1。

若是有Mi = 1,则有Mj = 1。

二、解题思路

1)先构建并初始一个长度为N的并查集数组,此时朋友圈的数量初始的长度;

2)遍历M二维数组,判断每一个学生之间的关系是否为1,为1则调合并操做将二者关联起来,同时对朋友圈的数量递减;

3)遍历完则直接返回朋友圈的数量。

三、代码实例:

class findUnionCollection{
    private int[] s; // 江湖人数的数组集
    int count; // 门派个数
 
 
// 构建江湖人士的数组集和门派个数
    public findUnionCollection(int length){
        s = new int[length];
        s = initCollection(s);
        count = length;
    }
 // 将每一个江湖人士设置为各自一派的管理者(根节点默认为-1)
    private int[] initCollection(int[] target){
        for (int i =0;i<target.length;i++){
            target[i]=-1;
        }
        return target;
    }
    
public int find(int x){
 
 // 当这个成员的所对应的值小于0,即为-1,则为门派的管理员,直接返回
        if(s[x]<0){
            return x;
        }
  // 若是不为0,则这个成员所对应的值是他的直接上层大佬
  // 此时能够去递归这个大佬的大佬,直到找到最终的管理员
      // 将各个门派的成员都指向最终管理员(实际就是路径压缩)
        return s[x] = find(s[x]);
    }
}
public void unionCollection(int child1,int child2){
 
// 先判断两个江湖人士是否所属一派,若是是,直接返回,无须合并
        if(find(child1)==find(child2)){
            return;
        }
// 以后咱们这个成员所属门派的管理员的深度进行对比
// 若是两个管理者的深度同样,就随机选一个管理员做为最终管理员,将加深其深度(减1)
// 若是child1的深度比child2的深度深,则将child2的管理员指向child1管理员
          if(s[find(child1)]<s[find(child2)]){
            s[find(child2)]=find(child1);
        }else {
            if(s[find(child1)]==s[find(child2)]){
                s[find(child2)]--;
            }
            s[find(child1)]=find(child2);
        }
因为每一次合并,都会减小一个门派,因此门派的数量也就递减
        count--;
    }
    
class Solution {
    public Solution(){
    }
    public static int findCircleNum(int[][] M) {
        int studentNums = M[0].length;
        findUnionCollection FUC = new findUnionCollection(studentNums);
        for(int i=0;i<studentNums-1;i++){
            for(int j = i+1; j<M[i].length;j++){
                if(M[i][j]==1){
                    FUC.unionCollection(i,j);
                }
            }
        }
        return FUC.getCount();
    }
    public static void main(String[] var0) {
        int[][] M = new int[][]{{1,1,0},{1,1,0},{0,0,1}};
        System.out.println("findUnionCollection:"+findCircleNum(M));
    }
}
复制代码
相关文章
相关标签/搜索