【数据结构进阶】并查集

并查集

正如它的名字一样,并查集(Union-Find)就是用来对集合进行 合并(Union)查询(Find) 操作的一种数据结构。
合并 就是将两个不相交的集合合并成一个集合。
查询 就是查询两个元素是否属于同一集合。

并查集的原理

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。

组成

并查集主要由一个整型数组pre[ ]和两个函数find( )、join( )构成。

数组 pre[ ] 记录了每个点的前驱节点是谁,函数 find(x) 用于查找指定节点 x 属于哪个集合,函数 join(x,y) 用于合并两个节点 x 和 y 。

现实意义

比如现在有这样的情况:

目前饭局有10个人,没两个人之间都可能存在远方亲戚关系,如何将个人分成不同的家庭?现在将个人编号为{0,1,2,3,4,5,6,7,8,9}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xp2EqcYB-1672971490156)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672967653754.png)]

-1表示假设每个人之间都不存在亲戚关系

接下来,饭局中的人互相交谈,了解到自己的亲戚关系,因此可以形成不同的家庭。比如:当前是s1:{0,6,7,8},s2:{1,4,9},s3:{2,3,5}形成了不同的家庭。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VZrorsUR-1672971490158)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672967711197.png)]

用树模型表示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8NEgt0JH-1672971490159)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672967778007.png)]

因此规定并查集数组有下面的规定:

  1. 数组的下标对应集合中元素的编号
  2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数
  3. 数组中如果为非负数,代表该元素双亲在数组中的下标
  4. 如果此时0和1发现也有亲戚关系,那么s1和s2可以合并为一个新的家庭

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-04KJOESp-1672971490160)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672967818968.png)]

并查集需要有下面的接口

  • 查找元素属于哪个集合
  • 查看两个元素是否属于一个集合
  • 合并两个集合
  • 集合的个数

并查集的实现

创建一个并查集

并查集底层就是一个数组,数组内每个元素初始为-1。

public class unionfindset {
    public int[] elem;
    public int usedSize;

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

    public static void main(String[] args) {
        unionfindset unionfindset = new unionfindset(10);
        System.out.println(123);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1R8vPpbF-1672971490160)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672968167815.png)]

功能实现:

/**
     * 查找数据 x 的根结点
     * @param x
     * @return 下标
     */
    public int findRoot(int x){
        if (x<0){
            throw  new IndexOutOfBoundsException("下标不合法,是负数");
        }
        while (elem[x]>=0){
            x = elem[x]; //1 0
        }
        return x;
    }

    /**
     * 查询x1 和 x2 是不是同一个集合
     * @param x1
     * @param x2
     * @return
     */
    public boolean isSameUnionFindSet(int x1,int x2){

        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return true;
        }else{
            return false;
        }
    }
 /**
     * 这是合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1,int x2){
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return;
        }else{
            elem[index1] = elem[index1]+elem[index2];
            elem[index2]=index1;
        }

    }

    public int getCount(){
        int count = 1;
        for (int x:elem){
            if (x<0){
                count++;
            }
        }
        return count;
    }

    public void print(){
        for (int x:elem){
                System.out.print(x+" ");
        }
    }

并查集的应用

省份数量

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

来源:力扣(LeetCode)
链接:省份数量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bHwZJlCa-1672971490161)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672971156494.png)]

class Solution {
   public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        unionfindset ufs = new unionfindset(n);
        for(int i = 0;i < isConnected.length;i++) {
            for(int j = 0;j < isConnected[i].length;j++) {
                if(isConnected[i][j] == 1) {
                    ufs.union(i,j);
                }
            }
        }
        return ufs.getCount();
    }

}
class unionfindset {
    public int[] elem;
    public int usedSize;

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

    /**
     * 查找数据 x 的根结点
     * @param x
     * @return 下标
     */
    public int findRoot(int x){
        if (x<0){
            throw  new IndexOutOfBoundsException("下标不合法,是负数");
        }
        while (elem[x]>=0){
            x = elem[x]; //1 0
        }
        return x;
    }

    /**
     * 查询x1 和 x2 是不是同一个集合
     * @param x1
     * @param x2
     * @return
     */
    public boolean isSameUnionFindSet(int x1,int x2){

        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 这是合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1,int x2){
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return;
        }else{
            elem[index1] = elem[index1]+elem[index2];
            elem[index2]=index1;
        }

    }

    public int getCount(){
        int count = 0;
        for (int x:elem){
            if (x<0){
                count++;
            }
        }
        return count;
    }

    public void print(){
        for (int x:elem){
                System.out.print(x+" ");
        }
    }
}

等式方程的可满足性

给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。

只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。

来源:力扣(LeetCode)
链接:等式方程的可满足性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FGoEk71r-1672971490162)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672971191283.png)]

class Solution {



     //等式的满足性
    public boolean equationsPossible(String[] equations) {
        unionfindset ufs = new unionfindset(26);
        for(int i = 0; i < equations.length;i++) {
            if(equations[i].charAt(1) == '=') {
                ufs.union(equations[i].charAt(0)-'a',equations[i].charAt(3)-'a');
            }
        }
        for(int i = 0; i < equations.length;i++) {
            if(equations[i].charAt(1) == '!') {
                int index1 = ufs.findRoot(equations[i].charAt(0)-'a');
                int index2 = ufs.findRoot(equations[i].charAt(3)-'a');
                if(index1 == index2) return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        String[] str = {"a==b","b!=a"};
        //equationsPossible(str);
    }

}
class unionfindset {
    public int[] elem;
    public int usedSize;

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

    /**
     * 查找数据 x 的根结点
     * @param x
     * @return 下标
     */
    public int findRoot(int x){
        if (x<0){
            throw  new IndexOutOfBoundsException("下标不合法,是负数");
        }
        while (elem[x]>=0){
            x = elem[x]; //1 0
        }
        return x;
    }

    /**
     * 查询x1 和 x2 是不是同一个集合
     * @param x1
     * @param x2
     * @return
     */
    public boolean isSameUnionFindSet(int x1,int x2){

        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 这是合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1,int x2){
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return;
        }else{
            elem[index1] = elem[index1]+elem[index2];
            elem[index2]=index1;
        }

    }

    public int getCount(){
        int count = 0;
        for (int x:elem){
            if (x<0){
                count++;
            }
        }
        return count;
    }

    public void print(){
        for (int x:elem){
                System.out.print(x+" ");
        }
    }
}
  • 24
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
并查集(Disjoint Set)是一种数据结构,用于解决集合的合并和查找问题。在Python中可以使用类来实现并查集。引用展示了一个简单的并查集类的代码实现,其中包括了初始化集合、查找集合、合并集合和判断两个元素是否在同一个集合中的方法。另外,引用和展示了对并查集代码的优化,包括路径压缩和按秩合并等技巧,以提高并查集的效率。 在Python中使用并查集可以解决一些实际问题,如求解岛屿个数、朋友圈等。通过将问题转化为集合的合并和查找操作,可以使用并查集来高效地解决这些问题。 所以,如果你需要在Python中实现并查集,可以参考以上的代码实现和优化方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python 数据结构与算法——并查集](https://blog.csdn.net/itnerd/article/details/103916115)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [并查集Python版](https://blog.csdn.net/XZ2585458279/article/details/127274576)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孙宇航_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值