并查集专题

news2024/11/22 15:19:17

请添加图片描述
⭐️前言⭐️

本篇文章主要介绍与并查集相关的题目。

🍉欢迎点赞 👍 收藏留言评论 📝私信必回哟😁

🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

🍉博客中涉及源码及博主日常练习代码均已上传GitHub


请添加图片描述

📍内容导读📍

  • 🍅并查集
  • 🍅map实现并查集
  • 🍅数组实现并查集
  • 🍅省份数量
  • 🍅岛问题

🍅并查集

并查集就是用来对集合进行合并查询操作的一种数据结构。
合并就是将两个不相交的集合合并成一个集合。
查询就是查询两个元素是否属于同一个集合。

🍅map实现并查集

每个集合都以类似链表的形式(其实都是map的key->value指向)存在,并且链表的代表节点(尾节点指向自己)
判断两个元素是否属于同一个集合,就判断这两个元素对应的代表节点是否是同一个节点即可。
如果不是一个集合想要合并,从优化的角度来做,就让长度小的链表代表节点,指向长度长的链表代表节点。
以下是代码实现,通过两张表来完成这样的一个数据结构:
ancestor表存储节点及对应的代表节点
size表存储代表节点对应集合的元素个数

public class UnionFind_Code {
    public static class UnionFind<V> {
        public HashMap<V,V> represent;
        public HashMap<V,Integer> size;

        /*
        初始化时,每个节点就是单独一个集合,代表节点就是自己
         */
        public UnionFind(List<V> values) {
            represent=new HashMap<>();
            size=new HashMap<>();
            for(V cur:values) {
                represent.put(cur,cur);
                size.put(cur,1);
            }
        }
        /*
        给一个节点,往上到不能再往上,把代表返回(借助栈实现扁平优化,再查询时更快)
         */
        public V findRepresent(V cur) {
            Stack<V> path=new Stack<>();
            while (cur!=represent.get(cur)) {
                // 当cur不是代表节点时,记录路径并继续往上
                path.push(cur);
                cur=represent.get(cur);
            }
            while (!path.isEmpty()) {
                represent.put(path.pop(),cur);
            }
            return cur;
        }
        /*
        判断是否是同一个集合
         */
        public boolean isSameSet(V a,V b) {
            return represent.get(a)==represent.get(b);
        }
        /*
        合并集合
         */
        public void union(V a,V b) {
            V aRepresent=findRepresent(a);
            V bRepresent=findRepresent(b);
            if(aRepresent!=bRepresent) {
                int aSize=size.get(aRepresent);
                int bSize=size.get(bRepresent);
                if(aSize>=bSize) {
                    represent.put(bRepresent,aRepresent);
                    size.put(aRepresent,aSize+bSize);
                    size.remove(bRepresent);
                }else {
                    represent.put(aRepresent,bRepresent);
                    size.put(bRepresent,aSize+bSize);
                    size.remove(aRepresent);
                }
            }
        }
        
        public int sets() {
            return size.size();
        }
    }
}

🍅数组实现并查集

题目:

链接:https://www.nowcoder.com/questionTerminal/e7ed657974934a30b2010046536a5372
来源:牛客网

给定一个没有重复值的整形数组arr,初始时认为arr中每一个数各自都是一个单独的集合。请设计一种叫UnionFind的结构,并提供以下两个操作。

  1. boolean isSameSet(int a, int b): 查询a和b这两个数是否属于一个集合
  2. void union(int a, int b): 把a所在的集合与b所在的集合合并在一起,原本两个集合各自的元素以后都算作同一个集合
    [要求]
    如果调用isSameSet和union的总次数逼近或超过O(N),请做到单次调用isSameSet或union方法的平均时间复杂度为O(1)
public class Code_UnionFind {
    // 最大测试数据量
    public static int MAXN = 1000001;
    // 记录i节点的代表节点
    public static int[] represent=new int[MAXN];
    // 记录代表节点所在集合的元素个数
    public static int[] size=new int[MAXN];
    // 辅助数组用于实现扁平优化
    public static int[] help=new int[MAXN];

    /*
    初始化并查集
     */
    public static void init(int n) {
        for (int i = 0; i < n; i++) {
            represent[i]=i;
            size[i]=1;
        }
    }
    /*
    寻找集合代表点
     */
    public static int find(int i) {
        int hi=0;
        // 当i的代表节点不是i自己时,继续往上找代表节点,并记录路径所有节点
        while (i!=represent[i]) {
            help[hi++]=i;
            i=represent[i];
        }
        // 扁平优化,把路径上所有节点的代表节点都设为集合的代表节点
        while (hi-->=0) {
            represent[help[hi]]=i;
        }
        return i;
    }
    /*
    查询x和y是不是一个集合
     */
    public static boolean isSameSet(int x,int y) {
        return find(x)==find(y);
    }
    /*
    合并集合
     */
    public static void union(int x,int y) {
        int fx=find(x);
        int fy=find(y);
        if(fx!=fy) {
            if(size[fx]>=size[fy]) {
                size[fx]+=size[fy];
                represent[fy]=fx;
            }else {
                size[fy]+=size[fx];
                represent[fx]=fy;
            }
        }
    }
}

🍅省份数量

Leetcode题目:https://leetcode.cn/problems/number-of-provinces/

题解思路:
利用并查集数据结构,直接或者间接相连的城市,都放入一个集合中,最后求一共有几个集合,就是有几个省份。
遍历矩阵完成集合的合并,如果isConnected[i][j]=1,就放入一个集合中,遍历时j=i+1(因为1和3连接,3和1肯定也连接,没有必要再重复操作)。

代码实现:

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

    public class UnionFind {
        private int[] represent;
        private int[] size;
        private int[] help;
        private int sets;
        public UnionFind(int N) {
            represent=new int[N];
            size=new int[N];
            help=new int[N];
            sets=N;
            for(int i=0;i<N;i++) {
                represent[i]=i;
                size[i]=1;
            }
        }

        private int find(int i) {
            int hi=0;
            while(i!=represent[i]) {
                help[hi++]=i;
                i=represent[i];
            }
            while(hi-->0) {
                represent[help[hi]]=i;
            }
            return i;
        }

        public void union(int i,int j) {
            int f1=find(i);
            int f2=find(j);
            if(f1!=f2) {
                if(size[f1]>=size[f2]) {
                    size[f1]+=size[f2];
                    represent[f2]=f1;
                }else {
                    size[f2]+=size[f1];
                    represent[f1]=f2;
                }
                sets--;
            }
        }

        public int sets() {
            return sets;
        }
    }
}

🍅岛问题

题目:
给定一个二维数组matrix,里面的值不是1就是0,上、下、左、右相邻的1认为是一片岛,返回matrix中岛的数量
(递归解法 + 并查集解法1 + 并查集解法2)
1、递归解法
题解思路:

遇到1后,就把该位置上下左右所以能与之相接的位置都变为2,然后通过递归感染的方式,将所有独立范围内的1感染完成,岛屿数+1,当再次遇到1时再重复相同的操作,最后返回岛屿数量。
(该方法也是时间复杂度最优的解法,O(N*M))
代码实现:

class Solution {
    public static int numIslands(char[][] board) {
        int islands=0;
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if(board[i][j]=='1') {
                    islands++;
                    infect(board,i,j);
                }
            }
        }
        return islands;
    }

    // 从(i,j)位置出发,把所有连成一片的"1"字符,变成2
    private static void infect(char[][] board, int i, int j) {
        if(i<0||i== board.length||j<0||j==board[0].length||board[i][j]!='1') {
            return;
        }
        board[i][j]=2;
        infect(board,i-1,j);
        infect(board,i+1,j);
        infect(board,i,j-1);
        infect(board,i,j+1);
    }
}

2、并查集解法1
题解思路:

遍历棋盘,把所有与之相连的1合并到一个集合中,最后返回集合数就是岛屿数。

注意:
1.每个位置如果是1,则只需要判断其左边或者上边是否为1即可,如果是就合并
2.因为棋盘中存储都是’1’,合并的话没有办法区分,所以通过dot类来辅助完成。

代码实现:

class Solution {
    public static int numIslands(char[][] board) {
        int row=board.length;
        int col=board[0].length;
        Dot[][] dots=new Dot[row][col];
        List<Dot> dotList=new ArrayList<>();
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if(board[i][j]=='1') {
                    dots[i][j]=new Dot();
                    dotList.add(dots[i][j]);
                }
            }
        }
        UnionFind1 unionFind1=new UnionFind1<>(dotList);
        for (int j = 1; j < col; j++) {
            if(board[0][j-1]=='1'&&board[0][j]=='1') {
                unionFind1.union(dots[0][j-1],dots[0][j]);
            }
        }
        for(int i=1;i<row;i++) {
            if(board[i-1][0]=='1'&&board[i][0]=='1') {
                unionFind1.union(dots[i-1][0],dots[i][0]);
            }
        }
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                if(board[i][j]=='1') {
                    if(board[i][j-1]=='1') {
                        unionFind1.union(dots[i][j-1],dots[i][j]);
                    }
                    if(board[i-1][j]=='1') {
                        unionFind1.union(dots[i-1][j],dots[i][j]);
                    }
                }
            }
        }
        return unionFind1.size();
    }

    public static class Dot {

    }
    public static class UnionFind1<V> {
        public HashMap<V,V> represents;
        public HashMap<V,Integer> sizeMap;

        public UnionFind1(List<V> values) {
            represents=new HashMap<>();
            sizeMap=new HashMap<>();
            for(V cur:values) {
                represents.put(cur,cur);
                sizeMap.put(cur,1);
            }
        }

        private V findRepresent(V cur) {
            Stack<V> path=new Stack<>();
            while (cur!=represents.get(cur)) {
                path.push(cur);
                cur=represents.get(cur);
            }
            while (!path.isEmpty()) {
                represents.put(path.pop(),cur);
            }
            return cur;
        }

        public void union(V a,V b) {
            V aRepresent=findRepresent(a);
            V bRepresent=findRepresent(b);
            if(aRepresent!=bRepresent) {
                int aSize=sizeMap.get(aRepresent);
                int bSize=sizeMap.get(bRepresent);
                if(aSize>=bSize) {
                    represents.put(bRepresent,aRepresent);
                    sizeMap.put(aRepresent,aSize+bSize);
                    sizeMap.remove(bRepresent);
                }else {
                    represents.put(aRepresent,bRepresent);
                    sizeMap.put(bRepresent,aSize+bSize);
                    sizeMap.remove(aRepresent);
                }
            }
        }

        public int size() {
            return sizeMap.size();
        }
    }
}

3、并查集解法2
题解思路:

该方法其实就是把并查集通过数组的方式来实现,而不再借助map,并且通过将棋盘上的点映射到数组中的位置,便减少了辅助类dot的使用。
最后通过规则合并棋盘中的值,对应合并了数组中的数,返回并查集的集合数即为最终结果。
代码实现:

class Solution {
    public int numIslands(char[][] board) {
        int row=board.length;
        int col=board[0].length;
        UnionFind2 unionFind2=new UnionFind2(board);
        for (int j = 1; j < col; j++) {
            if(board[0][j-1]=='1'&&board[0][j]=='1') {
                unionFind2.union(0,j-1,0,j);
            }
        }
        for (int i = 1; i < row; i++) {
            if(board[i-1][0]=='1'&&board[i][0]=='1') {
                unionFind2.union(i-1,0,i,0);
            }
        }
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                if(board[i][j]=='1') {
                    if(board[i-1][j]=='1') {
                        unionFind2.union(i-1,j,i,j);
                    }
                    if(board[i][j-1]=='1') {
                        unionFind2.union(i,j-1,i,j);
                    }
                }
            }
        }
        return unionFind2.sets;
    }
    public static class UnionFind2 {
        private int[] represent;
        private int[] size;
        private int[] help;
        private int col;
        private int sets;
        
        public UnionFind2(char[][] board) {
            col=board[0].length;
            sets=0;
            int row= board.length;
            int len=row*col;
            represent=new int[len];
            size=new int[len];
            help=new int[len];
            for (int r = 0; r < row; r++) {
                for (int c = 0; c < col; c++) {
                    if(board[r][c]=='1') {
                        int i=index(r,c);
                        represent[i]=i;
                        size[i]=i;
                        sets++;
                    }
                }
            }
        }
        
        private int index(int r,int c) {
            return r*col+c;
        }
        
        private int find(int i) {
            int hi=0;
            while (i!=represent[i]) {
                help[hi++]=i;
                i=represent[i];
            }
            while (hi-->0) {
                represent[help[hi]]=i;
            }
            return i;
        }
        
        public void union(int r1,int c1,int r2,int c2) {
            int i1=index(r1,c1);
            int i2=index(r2,c2);
            int f1=find(i1);
            int f2=find(i2);
            if(f1!=f2) {
                if(size[f1]>=size[f2]) {
                    size[f1]+=size[f2];
                    represent[f2]=f1;
                }else {
                    size[f2]+=size[f1];
                    represent[f1]=f2;
                }
                sets--;
            }
        }
    }
}

⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁

请添加图片描述

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

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

相关文章

阿里「通义千问」内测详细使用体验

名人说&#xff1a;一花独放不是春&#xff0c;百花齐放花满园。——《增广贤文》 作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、简要介绍二、分类问题测试0️⃣自我介绍1️⃣生成内容2️⃣回答问题3️⃣对话协…

springboot+vue大学生租房系统(java项目源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的大学生租房系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌…

太稳了,支付系统就该这么设计

支付中心系统对内为各个业务线提供统一的支付、退款等服务&#xff0c;对外对接三方支付或银行服务实现资金的流转。如下图&#xff1a; 大部分公司基本都是这样的架构&#xff0c;主要有以下几方面的优点&#xff1a; 形成统一支付服务&#xff0c;降低业务线接入成本及重复研…

数据结构-树,森连,二叉树之间的转换

树》二叉树 1.给兄弟加线 2.给出长子外的孩子去线 3.层次调整 &#xff08;整体向左偏移45&#xff09; eg&#xff1a; 1.给兄弟加线&#xff1a; 2.给处长紫外的孩子去线 3.层次调整&#xff0c;整体向左偏移45 &#xff08;由兄弟转化来的孩子都是右节点&#xff0c…

DJ6-5 目录管理

目录 6.5.1 文件控制块和索引结点 1、文件控制块 FCB 2、索引节点 6.5.2 简单文件目录 1、单级目录结构 2、二级目录结构 3、树形目录结构 6.5.3 目录查询技术 1、线性检索法 2、Hash 方法 文件目录&#xff1a;是指由文件说明索引组成的用于文件检索的特殊文件…

chatgpt赋能python:Python文件复制到指定文件夹——实现简单又高效的文件操作

Python 文件复制到指定文件夹——实现简单又高效的文件操作 如今&#xff0c;人们对于数据的需求越来越多&#xff0c;因此在编程过程中&#xff0c;对于文件的操作也变得越来越重要。而Python作为一种高效而简洁的编程语言&#xff0c;其文件操作也是十分出色的。本文将会带领…

C/C++ ---- 内存管理

目录 C/C内存分布 常见区域介绍 经典习题&#xff08;读代码回答问题&#xff09; 选择题 填空题 C语言内存管理方式 malloc/free calloc realloc C内存管理方式 new和delete操作内置类型 new和delete操作自定义类型 operator new和operator delete函数 new和dele…

Linux环境变量总结

Linux是一个多用户的操作系统。多用户意味着每个用户登录系统后&#xff0c;都有自己专用的运行环境。而这个环境是由一组变量所定义,这组变量被称为环境变量。用户可以对自己的环境变量进行修改以达到对环境的要求。 设置环境变量的方法 对所有用户生效的永久性变量 这类变…

K8s进阶6——pod安全上下文、Linux Capabilities、OPA Gatekeeper、gvisor

文章目录 一、Pod安全上下文1.1 配置参数1.2 案例11.2.1 dockerfile方式1.2.2 pod安全上下文方式 1.3 案例21.4 Linux Capabilities方案案例1案例2 二、pod安全策略2.1 PSP&#xff08;已废弃&#xff09;2.1.1 安全策略限制维度 2.2 OPA Gatekeeper方案2.2.1 安装Gatekeeper2.…

百度搜索迎来奇点 大模型掀起代际变革

每一轮技术革命掀起的浪潮&#xff0c;大部多数人还没来得及思考或者布局&#xff0c;已经消失于海浪中。机会是给有准备的人的&#xff0c;要发现新兴技术的亮点&#xff0c;并立足自身去积极拥抱它&#xff0c;最后转化为自身前进的动力&#xff0c;跨越周期&#xff0c;迎来…

网站出现403 Forbidden错误的原因以及怎么解决的方法

这几天刚接手一批新做的网站&#xff0c;在访问网站的时候&#xff0c;会时不时的出现403 Forbidden错误&#xff0c;浏览器会给出403 Forbidden错误提示&#xff0c;在打开Access Error中列出的URL之后, 出现以下错误&#xff1a; 403 Forbidden Access to this resource on…

SAP工具箱 批量下载指定表数据到EXCEL

点击蓝字 关注我们 一 前言 下载指定表内容到指定的EXCEL是一个比较简单的程序.但仔细考虑这个程序,还是可以在细节上找出一些关注点 多表内容同时下载,每个表生成一个文件多表选择时,先查看表的记录数大表下载时,拆分下载拆分到不同的文件中拆分到同一个文件中的不同的工作表下…

windows server 2016 ftp搭建详细教程

一.什么是FTP&#xff1f; FTP(File Transfer Protocol)是TCP/IP网络上两台计算机传送文件的协议&#xff0c;使得主机间可以共享文件。 接下来我给大家分享快速搭建FTP服务器的方法。 二.安装FTP服务器 1.进入服务器系统打开“服务器管理器”&#xff0c;点击“添加角色和功…

【JavaSE】Java基础语法(二十三):递归与数组的高级操作

文章目录 1. 递归1.1 递归1.2 递归求阶乘 2. 数组的高级操作2.1 二分查找2.2 冒泡排序2.3 快速排序2.4 Arrays (应用) 1. 递归 1.1 递归 递归的介绍 以编程的角度来看&#xff0c;递归指的是方法定义中调用方法本身的现象把一个复杂的问题层层转化为一个与原问题相似的规模较…

C语言2:说心里话

描述 分两次从控制台接收用户的两个输入&#xff1a;第一个内容为“人名”&#xff0c;第一个内容为“心里 话”。 然后将这两个输入内容组成如下句型并输出出来&#xff1a; 1.(人名&#xff09;&#xff0c;I want to say&#xff0c;(心里话 2. 输入输出示例: 输入&#xff…

MybatisPlus SpringCloud Docker RabbitMQ ElasticSearch、Redis高级技术,分布式事务的综合应用

一、配置SpringCloud中的网关 1. nginx搭建 搭建好了启动nginx.exe即可出静态页面图 1.网关搭建 server:port: 10010 spring:application:name: api-gatewaycloud:nacos:server-addr: localhost:8848gateway:routes: #用户服务的路由&#xff1a;什么样的请求&#xff0c;让网…

Emacs之定制化mode line(第一百零二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

第二期:链表经典例题(两数相加,删除链表倒数第N个节点,合并两个有序列表)

每道题后都有解析帮助你分析做题&#xff0c;答案在最下面&#xff0c;关注博主每天持续更新。 PS&#xff1a;每道题解题方法不唯一&#xff0c;欢迎讨论&#xff01; 1.两数相加 题目描述 给你两个非空的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照逆序的方式…

【Vue】二:Vue核心处理---模板语法

文章目录 1.模板语法---插值2.模板语法---指令语法2.1v-once2.2 v-bind2.3 v-model2.4 v-on 3.MVVM4.事件回调函数中的this 1.模板语法—插值 {{可以写什么}} &#xff08;1&#xff09;在data中声明的变量&#xff0c;函数 &#xff08;2&#xff09;常量 &#xff08;3&…

【蓝桥杯省赛真题22】python剩余空间问题 青少年组蓝桥杯比赛python编程省赛真题解析

目录 python剩余空间问题 一、题目要求 1、编程实现 二、解题思路