并查集详解

news2025/1/13 13:14:09

1.并查集原理

某公司今年校招全国总共招生10人,西安招4人,成都招3人,武汉招3人,10个人来自不同的学校,起先互不相识,每个学生都是一个独立的小团体,现给这些学生进行编号:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 给以下数组用来存储该小集体,数组中的数字代表:该小集体中具有成员的个数。

 毕业后,学生们要去公司上班,每个地方的学生自发组织成小分队一起上路,于是:
西安学生小分队s1={0,6,7,8},成都学生小分队s2={1,4,9},武汉学生小分队s3={2,3,5}就相互认识了,10个人形成了三个小团体。假设有三个群主0,1,2担任队长,负责大家的出行。

 一趟火车之旅后,每个小分队成员就互相熟悉,成为了一个朋友圈。

从上图可以看出:编号6,7,8同学属于0号小分队,该小分队中有4人(包含队长0);编号为4和9的同学属于1号小分队,该小分队有3人(包含队长1),编号为3和5的同学属于2号小分队,该小分队有3个人(包含队长1)。

  • 数组的下标对应集合中元素的编号
  • 数组中如果为负数,负号代表根,数字代表该集合中元素个数
  • 数组中如果为非负数,代表该元素双亲在数组中的下标

在公司工作一段时间后,西安小分队中8号同学与成都小分队1号同学奇迹般的走到了一起,两个小圈子的学生相互介绍,最后成为了一个小圈子:

通过以上例子可知,并查集一般可以解决一下问题:
1.查找元素属于哪个集合
沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)
2.查看两个元素是否属于同一个集合

 沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
3.将两个集合归并成一个集合

将两个集合中的元素合并,将一个集合名称改成另一个集合的名称
4.集合的个数

2.并查集实现

package test;

import java.util.Arrays;

public class UnionFindSet {
    public int[] elem;
    public int usedsize;

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

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

    //查询x y 是不是同一个集合
    public boolean isSameUnionFindSet(int x, int y) {
        int index1 = findRoot(x);
        int index2 = findRoot(y);
        if (index1 == index2) {
            return true;
        }
        return false;
    }

    //合并
    public void union(int x, int y) {
        int index1 = findRoot(x);
        int index2 = findRoot(y);
        if (index1 == index2) {
            return;
        }
        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 + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        UnionFindSet unionFindSet = new UnionFindSet(10);
        unionFindSet.union(0, 6);
        unionFindSet.union(0, 7);
        unionFindSet.union(0, 8);
        unionFindSet.union(1, 4);
        unionFindSet.union(1, 9);
        unionFindSet.union(2, 3);
        unionFindSet.union(2, 5);
        unionFindSet.union(8, 1);
        unionFindSet.print();
        System.out.println("查找是不是同一个集合");
        System.out.println(unionFindSet.isSameUnionFindSet(6, 9));
        System.out.println(unionFindSet.isSameUnionFindSet(8, 2));
    }
}

3.例题

1.家族

package test;

import java.util.Arrays;

public class UnionFindSet {
    public int[] elem;
    public int usedsize;

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

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

    //查询x y 是不是同一个集合
    public boolean isSameUnionFindSet(int x, int y) {
        int index1 = findRoot(x);
        int index2 = findRoot(y);
        if (index1 == index2) {
            return true;
        }
        return false;
    }

    //合并
    public void union(int x, int y) {
        int index1 = findRoot(x);
        int index2 = findRoot(y);
        if (index1 == index2) {
            return;
        }
        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 + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        UnionFindSet unionFindSet = new UnionFindSet(10);
        unionFindSet.union(0, 6);
        unionFindSet.union(0, 1);
        unionFindSet.union(3, 7);
        unionFindSet.union(4, 8);
        System.out.println("以下是不是亲戚?");
        boolean flg = unionFindSet.isSameUnionFindSet(1, 6);
        if (flg) {
            System.out.println("是亲戚");
        } else {
            System.out.println("不是亲戚");
        }
    }
}

2.省份数量

class UnionFindSet {
    public int[] elem;
    public int usedsize;

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

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

    //查询x y 是不是同一个集合
    public boolean isSameUnionFindSet(int x, int y) {
        int index1 = findRoot(x);
        int index2 = findRoot(y);
        if (index1 == index2) {
            return true;
        }
        return false;
    }

    //合并
    public void union(int x, int y) {
        int index1 = findRoot(x);
        int index2 = findRoot(y);
        if (index1 == index2) {
            return;
        }
        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 + " ");
        }
        System.out.println();
    }

}

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

 3.等式方程的可满足性

 

class UnionFindSet {
    public int[] elem;
    public int usedsize;

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

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

    //查询x y 是不是同一个集合
    public boolean isSameUnionFindSet(int x, int y) {
        int index1 = findRoot(x);
        int index2 = findRoot(y);
        if (index1 == index2) {
            return true;
        }
        return false;
    }

    //合并
    public void union(int x, int y) {
        int index1 = findRoot(x);
        int index2 = findRoot(y);
        if (index1 == index2) {
            return;
        }
        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 + " ");
        }
        System.out.println();
    }

}

class Solution {
    public boolean equationsPossible(String[] equations) {
        UnionFindSet unionFindSet = new UnionFindSet(26);
        for (int i = 0; i < equations.length; i++) {
            if (equations[i].charAt(1) == '=') {
                unionFindSet.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 = unionFindSet.findRoot(equations[i].charAt(0) - 'a');
                int index2 = unionFindSet.findRoot(equations[i].charAt(3) - 'a');
                if (index1 == index2) {
                    return false;
                }
            }
        }
        return true;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/112152.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

156. 如何在 SAP UI5 应用里显示 PDF 文件

SAP 不少标准应用都可以在业务流程进行到某个阶段,根据系统里的业务数据和 SAP 事先开发好的表单模板,生成最终的 PDF 文件并显示在应用页面上。 本文介绍一种在 SAP UI5 页面里嵌入显示 PDF 文件内容的方式,效果如下。 点击屏幕右上角的下载图标,可以将这个显示的 PDF 下…

ASP.NET 企业人力资源管理系统源码 HR源码 前端bootstrap框架开发

中小型企业HR人力资源管理系统源码带使用手册和操作说明 了解更多&#xff0c;可私信我&#xff01; 【程序语言】&#xff1a;.NET 【数据库】&#xff1a;SQL SERVER 2008 【运行环境】&#xff1a;WINDOWSIIS 【其他】&#xff1a;前端bootstrap框架 运行环境&#xff1…

学习记录-mybatis+vue+elementUi实现分页查询(前端部分)

前端这一块最方便的莫过于是element已经提供好了 接口&#xff0c;三个最关键的接口这里首先解决第一个&#xff0c;总数。 //总记录数totalCount:100,我直接在data中将其先初始化为100&#xff0c;之后直接在响应中设置&#xff0c;这是从后端查询到的值&#xff0c;不需要任何…

华为被迫开源,从认知到落地SpringBoot企业级实战手册(完整版)

前言 本手册重在引导读者进入真实的项目开发体验&#xff0c;围绕Spring Boot技术栈全面展开&#xff0c;兼顾相关技术的知识拓展&#xff0c;由浅入深&#xff0c;步步为营&#xff0c;使读者既能学习基础知识&#xff0c;又能掌握. 一定的开发技巧。本书的目标是让读者拥有一…

图文详解 (Kubernetes)K8S 和 容器中的退出状态码含义和原因及解决方法

图文详解 (Kubernetes)K8S 和 容器中的退出状态码含义和原因及解决方法。 什么是容器退出码 当容器终止时,容器引擎使用退出码来报告容器终止的原因。如果您是 Kubernetes 用户,容器故障是 pod 异常最常见的原因之一,了解容器退出码可以帮助您在排查时找到 pod 故障的根本…

Hadoop3.3.4最新版本安装分布式集群部署

Index of /dist/hadoop/commonhttps://archive.apache.org/dist/hadoop/common 集群规划&#xff1a; 注意&#xff1a; NameNode和SecondaryNameNode不要安装在同一台服务器ResourceManager也很消耗内存&#xff0c;不要和NameNode、SecondaryNameNode配置在同一台机器上。 …

PHP 变量

变量是用于存储信息的"容器"&#xff1a; 实例 <?php $x5; $y6; $z$x$y; echo $z; ?> 运行实例 与代数类似 x5 y6 zxy 在代数中&#xff0c;我们使用字母&#xff08;如 x&#xff09;&#xff0c;并给它赋值&#xff08;如 5&#xff09;。 从上面的…

echarts的grid——图表的位置配置

首先还是先认识grid&#xff0c;要弄清楚grid是哪一块区域&#xff0c;这就牵扯到对echarts图表元素的基本认识。为此&#xff0c;我做了一个总结&#xff0c;如图所示&#xff1a; 数学里的笛卡尔坐标系分为直角坐标系 和斜坐标系。而grid只适用于直角坐标系&#xff01; 我们…

分布式理论之分布式互斥

写在前面 本文一起看下分布式理论中的分布式互斥&#xff08;distributed mutual[ˈmjutʃuəl] exclusion&#xff09;问题&#xff0c;以及解决该问题相关算法。 1&#xff1a;什么是分布式互斥 我们先看下什么是临界资源&#xff08;critical resource&#xff09;&#…

如何提高苹果商店ASA广告的展示份额

众所周知&#xff0c;APP获得曝光后&#xff0c;才会有用户的点击率和下载&#xff0c;接下来柚鸥ASO会告诉大家&#xff0c;如何在保障ROI&#xff08;是指投入成本跟获得的收益的比值&#xff09;的情况下&#xff0c;为ASA获得最大的展示份额。 CPM是指通过商家付费&#x…

【nowcoder】笔试强训Day2

目录 一、选择题 二、编程题 2.1排序子序列 2.2倒置字符串 一、选择题 1.A 派生出子类 B &#xff0c; B 派生出子类 C &#xff0c;并且在 java 源代码有如下声明&#xff1a; 1. A a0new A(); 2. A a1new B(); 3. A a2new C(); 问以下哪个说法是正确的&#xff08;&…

19. 【gRPC系列学习】拦截器Interceptor

无论客户端还是服务端在远程调用前后执行一些通用逻辑,例如Gin框架,实现日志、监控、认证、Recover等通用逻辑,gRPC也提供这样的机制。 拦截器分为:一元拦截器、流拦截器,这两种拦截器在客户端、服务端都可以使用。拦截器的执行流程如图:其中InHandlerx与outHandlerx是同…

1 月 25 日见|Flutter Forward 活动日程表正式发布!

2023 年 1 月 25 日 (正月初四)&#xff0c;我们将在肯尼亚首都内罗毕举办 Flutter Forward 大会&#xff0c;并同时开启线上直播。本次活动将为展示最新的 Flutter 技术更新&#xff0c;包括一个主题演讲以及多个技术演讲和线上问答&#xff0c;全方位展示 Flutter 如何推动 U…

B+树 [数据结构与算法][Java]

B树 B树是B树的一种变形 我们通过一颗四阶B树来理解认识一下B树:(如下:) 我们其实从图上就可以看出B树和B树是有很多不同之处的 比如我们的B树中将叶子结点层的所有结点使用一个链表串联了起来B树中对于非叶子结点都是只是存储的索引(指针), 并没有存储关键字, 所以我们最终查…

离散数学数理逻辑部分

前言 本文创作的起因是&#xff0c;经历了离散数学的学习&#xff0c;深感学习离散之艰辛。所以产生了写一些内容帮助大家期末复习。虽然在csdn发表本文&#xff0c;有些不太合适&#xff0c;但是还是相信本文的质量和内容&#xff0c;可以给正在学习离散数学的大学生提供一些…

vscode使用shift+alt+f格式化html文件时不生效

关于vscode配置相关文章&#xff08;方便作者之后复习&#xff09;: VScode如何在敲代码时自动导入包怎么在VScode中写代码模板【以创建express模板为例】如何根据项目的eslint去配置vscode的setting 1-1 下载Beautify插件 该插件已停止维护&#xff0c;输入下载其他插件 1-2 在…

【Linux】进程概念(上)

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;冯诺依曼体…

1754. 构造字典序最大的合并字符串

摘要 1754. 构造字典序最大的合并字符串 一 贪心算法分析 题目要求合并两个字符串 word1 与 word2&#xff0c;且要求合并后的字符串字典序最大。首先需要观察一下合并的选择规律&#xff0c;假设当前需要从 word1​ 的第 i 个字符和 word2​ 的第 j个字符选择一个字符加入到…

24. 【gRPC系列学习】gRPC安全认证-TLS认证

TLS认证是gRPC比较常见的方式,利用PKI体系,生成客户端证书、服务端证书、以及CA证书,在交互期间进行身份验证,经秘钥协商后,以对称秘钥进行加密通信,保证数据隐私。Basic、Jwt都是要自实现,TLS是官方支持,操作简单。 1. TLS原理 双向认证简单来讲:服务端验证客户端证书…

前端小知识:返回浏览器上一页(back、go、referrer)

官方文档&#xff08;document.referrer&#xff09;&#xff1a; https://developer.mozilla.org/zh-CN/docs/Web/API/Document/referrer   官方文档&#xff08;history.back&#xff09; https://developer.mozilla.org/zh-CN/docs/Web/API/History/back   官方文档&#…