详解 并查集(图+代码+例题)

一.概念

并查集,顾名思义,可以进行合并(并)查找(查)集合。我们用并查集可查找某一个元素是不是与另一个元素在同一个集合里,这些元素往往通过某一个规则合并在一起。

并查集一个集合就是一颗树,它是用树这种数据结构来实现的。

下图:123是一个集合,45是一个集合。

二.代码实现

1.定义

我们要一个数组去模拟树来存储点,每一个点存储的是其父节点的下标,如果一个点没有父节点是祖先节点,那么这个数记录的是负数,这个负数的绝对值是一整颗树的数量。

在定义数组时我们要注意数组的数量,看看数组的大小是不是会影响到程序功能的实现。

public int[] elem;

public UnionFindSet(int m){
    this.elem=new int[m];
    Arrays.fill(elem,-1);
}

2.找祖宗

从某一个节点向上走,找某一值是负数,那么这个值的下标就是要找的祖宗。

我们是用数组模拟树,不断通过存储的下标网上找即可。

public int findRoot(int x){
    while(elem[x]>=0){
        x=elem[x];
    }
    return x;
}

3.合并(并)

还是这个例子,我们要在3集合合并5集合,直观说就是将3和4连在一起,下图:

这个是连在一起后,数组的值:

代码上怎么实现?

找到5的祖宗节点,更新3的祖宗节点的值,让5的祖宗节点与3连接。

总结:1.找到两个要连接的点的祖宗节点;2.更新(x1)的祖宗节点的值;3.让其中一个节点(x2)的祖宗节点与另个节点(x1)连接。

注意,不能先连接,连接后的x2的祖宗节点的值会变化,无法完成x1的更新。

public void union(int x1,int x2){
    if(isSameUnionFindSet(x1,x2)){
        return ;
    }

    //1.找到两个要连接的点的祖宗节点
    int root1=findRoot(x1);
    int root2=findRoot(x2);

    //2.更新(x1)的祖宗节点的值
    elem[root1]=elem[root1]+elem[root2];

    //3.让其中一个节点(x2)的祖宗节点与另个节点(x1)连接
    elem[root2]=root1;
}

4.查找(查)

查找两个元素是不是一个集合,我们只需分别找到两个元素的祖宗节点,看看是不是同一个祖宗。

如果是,那么就这同一个集合;如果不是,那么就不在同一个集合。

代码实现很简单:

public boolean isSameUnionFindSet(int x1,int x2){
    int root1=findRoot(x1);
    int root2=findRoot(x2);

    return root1==root2;
}

5.路径压缩

路径压缩,把路径给压缩了,可以减少查找祖宗节点的时间。

使用路径压缩时,对于数组的初始化不能是-1。如果是-1,那么将无法更新。

还是上面的例子,将2345的祖宗全部设成1:

下面是路径压缩的代码实现:

public int find(int x){
    if(elem[x]!=x){
        elem[x]=find(elem[x]);
    }

    return elem[x];
}

public void union(int x1,int x2){
    elem[find(x1)]=find(x2);
}

三.例题

547. 省份数量

题目大意:给一些城市的连接关系,连在一起的就是同一个省份,求有多少个省份。

这个题就是典型的并查集问题,转换一下题目:求有多少个集合的个数。

这里我采用路径压缩的方法(代码简洁):

class Solution {
    public int[] elem;

    public int findCircleNum(int[][] isConnected) {
        int n=isConnected.length;
        elem=new int[n];
        for(int i=0;i<n;i++){
            elem[i]=i;
        }

        //合并
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(isConnected[i][j]==1){
                    //先判断在不在同一个集合
                    if(find(i)!=find(j)){
                        elem[find(i)]=find(j);
                    }
                }
            }
        }

        int sum=0;
        for(int i=0;i<n;i++){
            if(elem[i]==i){
                sum++;
            }
        }

        return sum;
    }

    public int find(int x){
        if(elem[x]!=x){
            elem[x]=find(elem[x]);
        }

        return elem[x];
    }
}

小结:这个题用DFS和BFS做会更好,但这里介绍的是并查集做法。

990. 等式方程的可满足性

题目描述可自行去LeetCode查看。

思路很简单,利用集合的特性就行。先让==的全部放入一个集合,再去查找!=的是不是在同一个集合,如果是,那么就错了;如果不是,就对了。

代码实现:

class Solution {
    public int[] elem;

    public boolean equationsPossible(String[] equations) {
        elem=new int[26];
        for(int i=0;i<26;i++){
            elem[i]=i;
        }

        for(int i=0;i<equations.length;i++){
            if(equations[i].charAt(1)=='='){
                int x1=equations[i].charAt(0)-'a';
                int x2=equations[i].charAt(3)-'a';
                elem[find(x1)]=find(x2);
            }
        }

        for(int i=0;i<equations.length;i++){
            if(equations[i].charAt(1)=='!'){
                int x1=equations[i].charAt(0)-'a';
                int x2=equations[i].charAt(3)-'a';
                if(find(x1)==find(x2)){
                    return false;
                }
            }
        }

        return true;
    }

    public int find(int x){
        if(elem[x]!=x){
            elem[x]=find(elem[x]);
        }

        return elem[x];
    }
}

例题后续还会更新,敬请期待!

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQL(Structured Query Language)是一种用于管理关系数据库的编程语言,它使用关系代数来操作和查询数据。下面我将详细解释一道SQL关系代数题目。 假设我们有两张表,一张是学生表(Student),包含学生的学号(StudentID)、姓名(Name)和班级(Class);另一张是成绩表(Score),包含学号(StudentID)和分数(Grade)。 题目要求:查询班级为1班的所有学生的学号和姓名,并按照分数从高到低的顺序进行排列。 首先,我们需要使用SELECT语句从学生表中选择班级为1班的学生信息。语句如下: SELECT StudentID, Name FROM Student WHERE Class = 1 接下来,我们需要将这些学生的学号和姓名与成绩表中的数据进行关联。我们可以使用JOIN语句来实现这一点。语句如下: SELECT Student.StudentID, Student.Name, Score.Grade FROM Student JOIN Score ON Student.StudentID = Score.StudentID 通过以上操作,我们得到了一个包含学生学号、姓名和分数的结果集。 最后,我们需要按照分数从高到低的顺序对结果集进行排序。可以使用ORDER BY语句来实现。语句如下: SELECT Student.StudentID, Student.Name, Score.Grade FROM Student JOIN Score ON Student.StudentID = Score.StudentID WHERE Student.Class = 1 ORDER BY Score.Grade DESC 通过以上操作,我们得到了一个按照分数从高到低排序的结果集,其中包含班级为1班的所有学生的学号和姓名。 这就是一道SQL关系代数例题的详细解答。希望能够帮助到您理解SQL关系代数的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值