常见的并查集题目

在这里插入图片描述

总结

并查集逻辑实现的优化有两种,第一种是查找时路径压缩,第二种是按秩合并,合并时将高度较小的树作为较高树的子树,从代码量来看,推荐使用路径压缩,可以参考lc 547. 省份数量的两种UnionFind写法

题目

1 LC990. 等式方程的可满足性

class Solution {
    class UnionFind{
        int[]p;
        int[]rank;
        
        public UnionFind(int n){
            p=new int[n];
            rank=new int[n];

            for(int i=0;i<n;i++){
                p[i]=i;
                rank[i]=1;
            }
        }

        public void union(int x,int y){
            int r1=find(x);
            int r2=find(y);
            if(r1==r2)return;
            if(rank[r1]>rank[r2]){
                p[r2]=r1;
            }else if(rank[r1]<rank[r2]){
                p[r1]=r2;
            }else{
                p[r2]=r1;
                rank[r1]++;
            }

        }
        public int find(int x){
            while(x!=p[x]){
                x=p[x];
            }
            return x;
        }
    }
    public boolean equationsPossible(String[] equations) {
        int n=equations.length;
        UnionFind uf=new UnionFind(26);
        for(int i=0;i<n;i++){
            String equation=equations[i];
            int a=equation.charAt(0)-'a';
            int b=equation.charAt(3)-'a';
            if(equation.charAt(1)=='=')
                uf.union(a,b);
        }

        for(int i=0;i<n;i++){
            String equation=equations[i];
            int a=equation.charAt(0)-'a';
            int b=equation.charAt(3)-'a';
            if(equation.charAt(1)=='!'){
                if(uf.find(a)==uf.find(b)){
                    return false;
                }
            }
            
        }
        return true;
    }
}

2 lc 547. 省份数量

class Solution {
    boolean[]vis;
    public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        vis=new boolean[n];
        int ans=0;
        for(int i=0;i<n;i++){
            if(!vis[i]){
                bfs(isConnected,i,n);
                ans++;
            }
            
        }
        return ans;
    }
    public void bfs(int[][] c,int i,int n){
        Deque<Integer>q=new ArrayDeque<>();
        q.offerLast(i);
        while(!q.isEmpty()){
            int size=q.size();
            for(int j=0;j<size;j++){
                int cno=q.pollFirst();
                if(vis[cno])continue;
                vis[cno]=true;
                for(int k=0;k<n;k++){
                    if(c[cno][k]==1){
                        q.offerLast(k);
                    }
                }
            }
        }
    }
    public void dfs(int[][] c,int i,int n){
        if(i<0||i>=n){
            return;
        }
        if(vis[i])return;
        vis[i]=true;
        for(int j=0;j<n;j++){
            if(c[i][j]==1){
                
                dfs(c,j,n);
            }
        }
    }
    // 当合并时,比较两个树的rank,将rank(也指高度)值大的树的根作为合并后的根节点。
    class UnionFind{
        int[]p;
        int[]rank;
        public int cnt;
        public UnionFind(int n){
            cnt=n;
            p=new int[n];
            rank=new int[n];

            for(int i=0;i<n;i++){
                p[i]=i;
                rank[i]=1;
            }
        }

        public void union(int x,int y){
            int r1=find(x);
            int r2=find(y);
            if(r1==r2)return;
            cnt--;
            if(rank[r1]>rank[r2]){
                p[r2]=r1;
            }else if(rank[r1]<rank[r2]){
                p[r1]=r2;
            }else{
                p[r2]=r1;
                rank[r1]++;
            }

        }
        public int find(int x){
            while(x!=p[x]){
                x=p[x];
            }
            return x;
        }
    }
    public int findCircleNum2(int[][] isConnected) {
        int n = isConnected.length;
        UnionFind uf = new UnionFind(n);
        for( int i = 0; i < n; ++i ) {
            for( int j = 0; j < n; ++j ) {
                if(i!=j&&isConnected[i][j]==1){
                    uf.union(i,j);
                }
            }
        }

        return uf.cnt;
    }
}
// 路径压缩版的并查集数据结构
 class UnionFind2{
        int[]p;
        int[]rank;
        public int cnt;
        public UnionFind2(int n){
            cnt=n;
            p=new int[n];
            rank=new int[n];

            for(int i=0;i<n;i++){
                p[i]=i;
                rank[i]=1;
            }
        }

        public void union(int x,int y){
            int r1=find(x);
            int r2=find(y);
            if(r1==r2)return;
            cnt--;
            p[r2]=r1;
            rank[r1]++;
            

        }
        public int find(int x){
            while(x!=p[x]){
                x=p[x];
            }
            return x;
        }
    }

3 1319. 连通网络的操作次数

复杂的思路:还要分别计算每个连通子图内的

class Solution {
    List<List<Integer>> list;
    boolean[] visited;
    int[]w;
    public int makeConnected2(int n, int[][] connections) {
        if (connections.length < n - 1) {
            return -1;
        }
        list=new ArrayList<>();
        visited=new boolean[n];
        w=new int[n];
        for(int i=0;i<n;i++){
            list.add(new ArrayList<>());
        } 
        int len=connections.length;
        for(int i=0;i<len;i++){
            int x=connections[i][0];
            int y=connections[i][1];
            list.get(x).add(y);
            list.get(y).add(x);

        }
        int need=-1;
        for(int i=0;i<n;i++){
            if(!visited[i]){
                dfs2(list,i);
                need++;
            }
        }

        return need;
    }
    public void dfs2(List<List<Integer>> list,int id){
        if(visited[id])return;
        visited[id]=true;
        for(int i=0;i<list.get(id).size();i++){
            dfs2(list,list.get(id).get(i));
        }
    }

    public int makeConnected(int n, int[][] connections) {
        if (connections.length < n - 1) {
            return -1;
        }
        UnionFind uf=new UnionFind(n);
        for(int i=0;i<connections.length;i++){
            int x=connections[i][0];
            int y=connections[i][1];
            uf.union(x,y);
        }
        int r=0;
        int cnt=0;
        for(int i=0;i<n;i++){
            if(uf.p[i]==i){
                cnt++;
            }
                
        }    
        return cnt-1;
    }
    class UnionFind{
        int[]p;

        public UnionFind(int n){
            p=new int[n];

            for(int i=0;i<n;i++){
                p[i]=i;
            }
        }
        public int find(int x){
            if(p[x]!=x){
                p[x]=find(p[x]);
            }
            return p[x];
        }
        public void union(int x,int y){
            int r1=find(x);
            int r2=find(y);
            if(r1==r2){
                return;
            }
            p[r1]=r2;

        }
        
    }

}

4 684. 冗余连接

class Solution {
    public int[] findRedundantConnection(int[][] edges) {
        UnionFind uf = new UnionFind( edges.length );
        int[] res = new int[2];
        for(int i=0;i<edges.length;i++){
            int[]e=edges[i];
            if(uf.find(e[0])==uf.find(e[1]))
                return new int[]{e[0],e[1]};
            uf.union(e[0],e[1]);
        }

        return res;
    }
    class UnionFind{
        int[]p;

        public UnionFind(int n){
            p=new int[n+1];

            for(int i=1;i<=n;i++){
                p[i]=i;
            }
        }
        public int find(int x){
            if(p[x]!=x){
                p[x]=find(p[x]);
            }
            return p[x];
        }
        public void union(int x,int y){
            int r1=find(x);
            int r2=find(y);
            if(r1==r2){
                return;
            }
            p[r1]=r2;

        }
        
    }
}

5 1361. 验证二叉树

class Solution {
    // 并查集用的集合列表
    List<Integer> p = new ArrayList<>();
    // 用于统计不相交的连通分支个数
    int cnt;
    public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
        // 用于标记各个孩子的父节点
        int[] father = new int[n];
        // 初始化
        Arrays.fill(father, -1);
        // 初始化并查集集合状态
        for(int i = 0; i < n; i++) p.add(i);
        // 初始化分支数
        cnt = n;
        // 遍历所有节点
        for(int i = 0; i < n; i++) {
            // 如果节点存在两个孩子,而且两个孩子相同,那么显然是错误的二叉树
            if(leftChild[i] == rightChild[i] && leftChild[i] != -1) return false;
            // 合并两个孩子
            if(!merge(father, i, leftChild[i]) || !merge(father, i, rightChild[i])) return false;
        }

        // 如果最后所有的节点组成一个连通分支,才是一棵树
        if(cnt == 1) return true;
        return false;

    }
    // 和并父亲和孩子节点,并判断逻辑
    private boolean merge(int[] father, int f, int c) {
        // 孩子是空的,直接返回
        if(c == -1) return true;
        // 孩子之前有爸爸了,就是错的
        if(father[c] != -1) return false;
        // 并查集查找两个集合的根
        int a = find(f), b = find(c);
        // 如果孩子和父亲已经存在于一个集合中,那么说明会产生环,返回错误
        if(a == b) return false;
        // 合并两个集合
        p.set(a, b);
        // 标记孩子的父亲是谁
        father[c] = f;
        // 连通分支数减一
        cnt --;
        return true;
    }
    // 并查集通用方法,找集合的根元素
    private int find(int x) {
        if(p.get(x) != x) {
            p.set(x, find(p.get(x)));
        }
        return p.get(x);
    }
}

作者:lippon
链接:https://leetcode.cn/problems/validate-binary-tree-nodes/solutions/663937/java-bing-cha-ji-yi-ci-bian-li-xiang-xi-6450j/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
根据上面这个方法自己写的一个并查集版本:
class Solution {
    public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
        UnionFind uf=new UnionFind(n);
        for(int i=0;i<n;i++){
            if(leftChild[i]!=-1&&!uf.union(leftChild[i],i)){
                return false;
            }
            if(rightChild[i]!=-1&&!uf.union(rightChild[i],i)){
                return false;
            }
        }
        return uf.cnt==1;
    }
    public class UnionFind{
        int[]p;
        int cnt;
        public UnionFind(int n){
            p=new int[n];
            for(int i=0;i<n;i++){
                p[i]=i;
                
            }
            cnt=n;
        }
        public int find(int x){//路径压缩
            if(p[x]!=x){
                p[x]=find(p[x]);
            }
            return p[x];
        }
        public boolean union(int x,int y){
            int rx=find(x);
            int ry=find(y);
            if(rx==ry)return false;//如果之前就已经在一个连通图中,则合并失败
            if(p[x]!=x)return false;//如果之前已经有爸爸了,则也合并失败
            p[rx]=ry;
            cnt--;
            return true;
        }
    }
   
}

6 LC399. 除法求值

class Solution {

    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        int n=values.length;

        HashMap<String, Integer>mp=new HashMap<>();
        UnionFind uf=new UnionFind(2*n);
        
        int k=0;
        for(int i=0;i<n;i++){
            String a=equations.get(i).get(0);
            String b=equations.get(i).get(1);
            if(!mp.containsKey(a))
                mp.put(a,k++);
            if(!mp.containsKey(b))
                mp.put(b,k++);

            
            uf.union(mp.get(a),mp.get(b), values[i]);
        }
        int m=queries.size();
        double[] ans=new double[m];
        
        for(int i=0;i<m;i++){
            String a=queries.get(i).get(0);
            String b=queries.get(i).get(1);
            
            int aa=mp.getOrDefault(a,-1);
            int bb=mp.getOrDefault(b,-1);
            if(aa==-1||bb==-1){
                ans[i]=-1.00;
                continue;
            }
            // bfs搜索
            ans[i]=uf.compute(aa,bb);
        }
        return ans;

    }

    class UnionFind{
        double[]w;
        int[]p;
        public UnionFind(int n){
            w=new double[n];
            p=new int[n];
            for(int i=0;i<n;i++){
                w[i]=1.0d;
                p[i]=i;
            }
        }


        public int find2(int x){
            
            while(p[x]!=x){
                int pn=p[x];
                p[x]=p[p[x]];
                w[x]=w[x]*w[pn];
                x=p[x];
            }

            return p[x];
        }
        public int find(int x) {
            if(x!=p[x]){
                int pn=p[x];
                p[x]=find(p[x]);
                w[x]*=w[pn];
            }
            return p[x];
        }

        public void union(int x,int y, double val){
            int rx=find(x);
            int ry=find(y);
            if(rx==ry)return;
            p[rx]=ry;
            w[rx]=w[y]*val/w[x];

        }

        public double compute(int x, int y){
            int rx=find(x);
            int ry=find(y);
            if(rx!=ry){
                return -1.0d;
            }
            return w[x]/w[y];
        }
    }
    public double[] calcEquation2(List<List<String>> equations, double[] values, List<List<String>> queries) {
        int n=values.length;

        HashMap<String, Integer>mp=new HashMap<>();

        double[][]mat=new double[2*n][2*n];

        int k=0;
        for(int i=0;i<n;i++){
            String a=equations.get(i).get(0);
            String b=equations.get(i).get(1);
            if(!mp.containsKey(a))
                mp.put(a,k++);
            if(!mp.containsKey(b))
                mp.put(b,k++);

            mat[mp.get(a)][mp.get(b)]=values[i];
            mat[mp.get(b)][mp.get(a)]=1.0/values[i];

        }
        int m=queries.size();
        double[] ans=new double[m];

        for(int i=0;i<m;i++){
            String a=queries.get(i).get(0);
            String b=queries.get(i).get(1);
            
            int aa=mp.getOrDefault(a,-1);
            int bb=mp.getOrDefault(b,-1);
            if(aa==-1||bb==-1){
                ans[i]=-1.00;
                continue;
            }
            // bfs搜索
            ans[i]=bfs(mat,aa,bb);
        }
        return ans;
    }

    double bfs(double[][]mat,int a, int b){
        int n=mat.length;
        Deque<double[]>q=new LinkedList<>();
        q.offerLast(new double[]{a*1.00,1.00});
        boolean[]vis=new boolean[n];
        while(!q.isEmpty()){
            int size=q.size();
            for(int i=0;i<size;i++){
                double poll[]=q.pollFirst();
                if(poll[0]==(double)b){
                    return poll[1];
                }
                double[]neighbors=mat[(int)poll[0]];
                for(int j=0;j<neighbors.length;j++){
                    if(!vis[j]&&(double)neighbors[j]!=(double)0.00){
                        vis[j]=true;
                        q.offerLast(new double[]{j, poll[1]*mat[(int)poll[0]][j]});
                    }
                }
            }
        }
        return -1.00;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

气膜篮球馆——智能场馆助力篮球梦想

篮球&#xff0c;作为青少年热爱的运动之一&#xff0c;不仅锻炼身体、塑造良好体态&#xff0c;更为结交朋友提供了丰富机会。如今&#xff0c;随着气膜篮球馆的兴起&#xff0c;这一运动在智能场馆中展现出更为舒适的一面&#xff0c;为篮球梦想的实现提供了强大助力。 随着人…

c语言:用结构体求平均分|练习题

一、题目 用c语言的结构体&#xff0c;求4位学生成绩的平均分 如图&#xff1a; 二、代码截图【带注释】 三、源代码【带注释】 #include <stdio.h> float aver();//声明平均分函数 void printScore();//声明打印函数 //设置结构体&#xff0c; struct student { …

【Linux Shell】4. 数组

文章目录 【 1. 数组的定义 】【 2. 读取数组 】【 3. 关联数组 】【 4. 获取数组中的所有元素 】【 5. 获取数组的长度 】 数组中可以存放多个值。 Bash Shell 只支持一维数组&#xff08;不支持多维数组&#xff09;&#xff0c;初始化时不需要定义数组大小。与大部分编程语言…

linux释放交换空间-Swap

确保剩余内存比swap内存空间大&#xff0c;再执行以下操作&#xff0c;否则会宕机&#xff01; 查看swap分区 swapon -s 会查看到你的swap分区位置 停止swap分区 停止swap分区是将swap内存释放到实际内存中 swapoff /dev/dm-1开启swap分区 swap分区内存成功释放到实际内…

年末汇总⭐️ 我是如何从学生切换到职场人身份的

目录 今日天气 阴 温度较低 一、Learning 二、Working 三、Living 章末 今日天气 阴 温度较低 小伙伴们大家好&#xff0c;冬已至 年将末 身为逮虾户的我看到大家的年末总结心中也不由得涌起一股创作热情&#xff0c;奈何没文化&#xff0c;只能按照…

linux中最常用的目录导航命令

文章目录 Linux中最常用的目录导航命令探索未知世界的cd进入刚才的目录快速返回家目录进入某用户的家目录结合CDPATH的妙用!$用shopt –s cdspell自动纠正cd命令的目录名输入错误 最常用的且没有之一的 ls命令格式不加任何参数使用-l显示更多细节使用-t按照时间排序使用-r按照时…

JAVA的引用与C++的指针有什么区别

JAVA的引用与C的指针有什么区别 1. Java值类型与引用类型1.1 变量初始化1.2 变量赋值1.3 函数传参 2. Java数据存储方式2.1 Java局部变量&&Java方法参数2.2 Java数组类型引用和对象2.3 String类型数据 3. Java引用类型3.1 强引用3.2 软引用3.3 弱引用3.4 虚引用 4. JAV…

关于TypeScript Interface你需要知道的10件事

TypeScript接口的10种使用场景——可能只有20%的web开发人员完全掌握它们 TypeScript中的接口是一个非常灵活的概念。除了抽象类的部分行为外&#xff0c;它还经常用于描述“对象的形状”。 必需的属性 在定义接口时&#xff0c;需要使用 interface 关键字: interface Use…

prometheus基本介绍

官网&#xff1a;https://prometheus.io/docs/introduction/overview/ 中文&#xff1a; https://www.prometheus.wang/ Prometheus 选择 Prometheus 并不是偶然&#xff0c;因为&#xff1a; • Prometheus 是按照 《Google SRE 运维之道》的理念构建的&#xff0c;具有实用…

css sourcemap 源代码映射

vue.config.js css: {// Enable CSS source maps.sourceMap: process.env.NODE_ENV ! production, }重新运行&#xff1a;yarn serve 效果&#xff1a;

基于sy3130光感入耳检测功能成功实现

基于sy3130光感入耳检测功能成功实现 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群赠送语音信号处理降噪算法,蓝牙耳机音频,DSP音频项目核心开发资料, 1 芯片介绍 2 电路实现 3 寄存器列表

部署清华ChatGLM-6B(Linux版)

引言 前段时间,清华公布了中英双语对话模型 ChatGLM-6B,具有60亿的参数,初具问答和对话功能。最!最!最重要的是它能够支持私有化部署,大部分实验室的服务器基本上都能跑起来。因为条件特殊,实验室网络不通,那么如何进行离线部署呢? 「部署环境」:CUDA Version 11.0,…

41.使用@Autowired注解自动装配的过程是怎样的?

使用@Autowired注解自动装配的过程是怎样的? 记住:@Autowired 通过Bean的后置处理器进行解析的 在创建一个Spring上下文的时候,在构造函数中进行注册AutowiredAnnotationBeanPostProcessor在Bean的创建过程中进行解析在实例化后预解析(解析@Autowired标注的属性、方法 比如…

【MySQL】数据库之MHA高可用

目录 一、MHA 1、什么是MHA 2、MHA 的组成 3、MHA的特点 4、MHA的工作原理 二、有哪些数据库集群高可用方案 三、实操&#xff1a;一主两从部署MHA 1、完成主从复制 步骤一&#xff1a;完成所有MySQL的配置文件修改 步骤二&#xff1a;完成所有MySQL的主从授权&#x…

MySQL检索距离当前最近的7个小时内,靠近每个时间点数据信息

MySQL检索距离当前最近的7个小时内&#xff0c;靠近每个时间点数据信息 如果你想在最近7个小时内找到每个时间点最接近的数据&#xff0c;即使某些时间点没有数据&#xff0c;你可以使用子查询和窗口函数。以下是一个示例查询&#xff1a; sqlCopy codeSELECTt.time_point,CO…

Hive用户自定义函数之UDF开发

在进行大数据分析或者开发的时候&#xff0c;难免用到Hive进行数据查询分析&#xff0c;Hive内置很多函数&#xff0c;但是会有一部分需求需要自己开发&#xff0c;这个时候就需要自定义函数了&#xff0c;Hive的自定义函数开发非常方便&#xff0c;今天首先讲一下UDF的入门开发…

Python初探:从零开始的编程奇妙之旅

一、Python是什么 Python是一门多用途的高级编程语言&#xff0c;以其简洁、易读的语法而脱颖而出。在深度学习领域&#xff0c;Python扮演着至关重要的角色。其丰富的科学计算库&#xff08;如NumPy、Pandas、Matplotlib&#xff09;和强大的深度学习框架&#xff08;如Tenso…

树莓派通过 I2C 驱动 LCD1602 液晶屏

前一阵用废旧的树莓派做了一个NAS服务器&#xff0c;手里还要一块闲置的LCD 1602 液晶屏模块&#xff0c;可以用来实时显示IP&#xff0c;作为NAS的服务器输出显示。 在树莓派上LCD 1602 液晶屏模块的使用非常简单&#xff0c;可以用 I2C 方式的驱动&#xff0c;只要使能0&…

【无标题】一本好书

(https://img-blog.csdnimg.cn/9e3c2302242149e4ac7dbc834bd5e027.jpg)(https://img-blog.csdnimg.cn/3427ed8648ff46bbb496ed512e0aa9cd.jpg1

2分钟了解什么是socket?

文章目录 概念比喻类型Socket 与 TCP、UDP的关系 概念 Socket 是提供网络通信功能的编程接口&#xff08;API&#xff09;&#xff0c;提供了网络通信的基本操作&#xff0c;允许程序或进程之间进行数据交换。是传输层协议的具体软件实现&#xff0c;它封装了协议底层的复杂实…