并查集解决图的连通性问题

news2024/12/26 23:24:35

并查集

  • 1. 定义
  • 2.并查集
  • 3.模板代码
  • 4. 力扣例题
    • 4.1 剑指 Offer II 118. 多余的边
    • 4.2 力扣695. 岛屿的最大面积

1. 定义

在计算机科学中,并查集(英文:Disjoint-set data structure,直译为不交集数据结构)是一种数据结构,用于处理一些不交集(Disjoint sets,一系列没有重复元素的集合)的合并及查询问题。并查集支持如下操作:
查询:查询某个元素属于哪个集合,通常是返回集合内的一个“代表元素”。这个操作是为了判断两个元素是否在同一个集合之中。
合并:将两个集合合并为一个。
添加:添加一个新集合,其中有一个新元素。添加操作不如查询和合并操作重要,常常被忽略。
由于支持查询和合并这两种操作,并查集在英文中也被称为联合-查找数据结构(Union-find data structure)或者合并-查找集合(Merge-find set)。

2.并查集

  • 可用于解决“图的动态连通性”问题;
  • 在解决某些问题时,可通过设置合适的“虚拟顶点”将元素进行分类,也就是同一类的元素互相连通,如此可将所有元素分为不同的连通域,继而进行其他操作;
  • 并查集底层使用一维数组存储多个“森林”,但实际只考虑某顶点的祖先顶点,将祖先顶点作为该顶点的直接父节点;
  • 底层一维数组的下标对应每个顶点的编号,其中存储该顶点的祖先顶点;
  • 初始情况下,顶点的直接父顶点为顶点本身;
  • 并查集代码涉及三个部分:1)初始化:构建父节点数组;2)查询操作:查询某顶点的父顶点;3)合并操作:将两棵不同子树合并为一棵,其中一个根节点作为合并后树的根节点;
  • 如果两个顶点连通,则他们的直接父节点是一样的;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.模板代码

package com.northsmile.union;


/**
 * @author NorthSmile
 * @version 1.0
 * @date 2023/4/19&21:46
 * 并查集模板代码
 */
public class Template {
    // 存储编号为i的顶点的祖先顶点编号
    public int[] parent;
    // 记录连通分量的数量
    public int count;
    /**
     * 初始化
     */
    public Template(int n){
        // 初始化节点数量为n,节点编号依次为0~n-1
        parent=new int[n+1];
        for (int i=1;i<=n;i++){
            // 初始状态:自己做自己的父节点
            parent[i]=i;
        }
        count=n;
    }

    /**
     * 查询某节点的祖先节点
     * @param v
     * @return
     */
    public int find(int v){
        // 说明此时该节点独立
        if (parent[v]==v){
            return v;
        } else {
            // 无路径压缩
//            return find(parent[v]);
            // 路径压缩
            parent[v]=find(parent[v]);
            return parent[v];
        }
    }

    /**
     * 合并顶点v、w
     * @param v
     * @param w
     */
    public void union(int v,int w){
        // 分别确定两顶点的祖先顶点
        int vP=find(v);
        int wP=find(w);
        if (vP==wP){
            return;
        }
        count--;
        // 以wP作为vp的父顶点,此时v的祖先顶点更新为wP,也就是将v、w为根节点的树合并为一棵树
        parent[vP]=wP;
    }

    /**
     * 判断v、w之间是否连通
     * 如果二者连通,他们的祖先顶点一定相同
     * @param v
     * @param w
     * @return
     */
    public boolean connected(int v,int w){
        // 分别确定两顶点的祖先顶点
        int vP=find(v);
        int wP=find(w);
        return vP==wP;
    }

    /**
     * 返回连通域的数量
     * @return
     */
    public int count(){
        return count;
    }
}

4. 力扣例题

4.1 剑指 Offer II 118. 多余的边

剑指 Offer II 118. 多余的边

树可以看成是一个连通且 无环 的 无向 图。给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi]表示图中在 ai 和 bi 之间存在一条边。 请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。

解题思路:

  • 树中任意两个节点之间是连通的;
  • n个节点的树中共有n-1条边;
  • edges数组长度为n说明最多有一个边是多余的;
  • 构成一条边的两个节点如果已经连通,说明二者直接父节点一样,如果此时再对这两个节点进行合并,说明这条边即是多余的;
class Solution {
    /**
     *
     * @param edges
     * @return
     */
    public int[] findRedundantConnection(int[][] edges) {
        int n=edges.length;
        UFData uf = new UFData(n);
        for (int i=0; i<n;i++){
            if (uf.find(edges[i][0])!=uf.find(edges[i][1])) {
                uf.union(edges[i][0], edges[i][1]);
            }else {
                return edges[i];
            }
        }
        return new int[0];
    }
}

class UFData{
    public int[] parent;
    public UFData(int n){
        parent=new int[n+1];
        for (int i=1;i<=n;i++){
            parent[i]=i;
        }
    }

    public int find(int v){
        if (v==parent[v]){
            return v;
        }else{
            parent[v]=find(parent[v]);
            return parent[v];
        }
    }

    public void union(int v,int w){
        int vP=find(v);
        int wP=find(w);
        if (vP==wP){
            return;
        }
        parent[vP]=wP;
    }

    public boolean connected(int v,int w){
        return find(v)==find(w);
    }
}

4.2 力扣695. 岛屿的最大面积

695. 岛屿的最大面积

给你一个大小为 m x n 的二进制矩阵 grid 。岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在
水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。岛屿的面积是岛上值为 1
的单元格的数目。计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int m=grid.length;
        int n=grid[0].length;
        UFData uf = new UFData(m * n);
        int[][] directions=new int[][]{{-1,0},{1,0},{0,-1},{0,1}};  // 上下左右
        boolean haveUnion=false;
        boolean haveOne=false;
        for (int i=0;i<m;i++){
            for (int j=0;j<n;j++){
                if (grid[i][j]==1){
                    haveOne=true;
                    // 连接邻域
                    for (int k=0;k<4;k++){
                        int cR=i+directions[k][0];
                        int cC=j+directions[k][1];
                        if (cR<0||cR>=m||cC<0||cC>=n){
                            continue;
                        }
                        if(grid[cR][cC]==1){
                            haveUnion=true;
                            uf.union(i*n+j,cR*n+cC);
                        }
                    }
                }
            }
        }
//        System.out.println(Arrays.toString(uf.size));
        return haveOne?haveUnion?uf.maxArea:1:0;
    }

    private static class UFData{
        public int[] parent;
        public int[] size;
        public int maxArea;
        public UFData(int n){
            parent=new int[n];
            size=new int[n];
            Arrays.fill(size,1);
            for (int i=0;i<n;i++){
                parent[i]=i;
            }
        }

        public int find(int v){
            if (v==parent[v]){
                return v;
            }else{
                parent[v]=find(parent[v]);
                return parent[v];
            }
        }

        public void union(int v,int w){
            int vP=find(v);
            int wP=find(w);
            if (vP==wP){
                return;
            }
            parent[vP]=wP;
            size[wP]+=size[vP];
            maxArea=Math.max(maxArea,size[wP]);
        }

        public boolean connected(int v,int w){
            return find(v)==find(w);
        }
    }
}

参考资料:

  • 麦克老师讲算法
  • labuladong算法小抄
  • 维基百科

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

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

相关文章

卷积神经网络轻量化教程之通道剪枝【附代码】

这两天自己手写了一个可以简单实现通道剪枝的代码&#xff0c;在这篇文章中也会对代码进行讲解&#xff0c;方便大家在自己代码中的使用。 如果还想学习YOLO系列的剪枝代码&#xff0c;可以参考我其他文章&#xff0c;下面的这些文章都是我根据通道剪枝的论文在YOLO上进行的实…

React框架----路由管理

文章目录 SPA路由路由基本使用路由组件与一般组件NavLink SPA single page application只有一个页面异步请求数据&#xff0c;局部更新页面本地局部切换页面&#xff08;不会向服务端加载整个页面&#xff09; 路由 地址栏路径与组件的对应关系切换路径&#xff0c;则切换组…

YOLOv5+单目跟踪(python)

YOLOv5单目跟踪&#xff08;python&#xff09; 1. 目标跟踪2. 测距模块2.1 设置测距模块2.2 添加测距 3. 主代码4. 实验效果 相关链接 1. YOLOv5单目测距&#xff08;python&#xff09; 2. YOLOv7单目测距&#xff08;python&#xff09; 3. YOLOv7单目跟踪&#xff08;pytho…

JDBC详解(一):JDBC概述

JDBC概述 前言一、数据的持久化1、概念2、应用 二、Java中的数据存储技术三、JDBC介绍四、JDBC体系结构五、JDBC程序编写步骤 前言 本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注博主&#xff01;也许一个人独行&#…

全志 Orange Pi相关网站集

Orange Pi 系统安装的常识 看到有教程说android系统需要用win32diskimager才能成功烧写运行镜像名称与版本的对应关系 ubuntu版本号代号16.04Xenial Xerus&#xff08;好客的非洲地鼠&#xff09;18.04Bionio Beaver &#xff08;仿生海狸}20.04Focal Fossa &#xff08;类似…

DFIG控制10-b: 双馈发电机的转矩方程推导

DFIG控制10-b 双馈发电机的转矩方程推导 接上DFIG控制10&#xff1a; 双馈发电机的动态模型_Fantasy237的博客&#xff0c;DFIG的转矩方程和推导。 &#xff08;字数限制&#xff0c;只能放在新的一篇博文里了。。&#xff09; 转矩方程 定子αβ静止坐标系 从三相坐标系下…

Linux网络套接字(二)

学习任务&#xff1a; 继网络套接字&#xff08;一&#xff09;&#xff0c;继续学习套接字socket编程接口&#xff08;已经学习了socket和bind&#xff09;&#xff0c;实现TCP客户端/服务器(单连接版本, 多进程版本, 多线程版本&#xff0c;进程或线程池版本)&#xff0c;并且…

JavaScript(JS)-1.JS基础知识

1.JavaScript概念 (1)JavaScript是一门跨平台&#xff0c;面向对象的脚本语言&#xff0c;来控制网页行为的&#xff0c;它能使网页可交互 (2)W3C标准&#xff1a;网页主要由三部分组成 ①结构&#xff1a;HTML负责网页的基本结构&#xff08;页面元素和内容&#xff09;。 …

SPI协议——同步全双工串行通信方式

文章目录 前言一、简要介绍1、优点2、缺点 二、信号线和连接方式1、四根信号线2、连接方式2.1 多NSS形式2.2 菊花链形式 三、SPI配置1、时钟极性CPOL2、时钟相位CPHA3、四种模式4、数据大小5、其他配置参数 四、通信过程 前言 2023.4.22 阴 一、简要介绍 SPI&#xff1a;Seri…

什么是分组卷积、深度可分离卷积?附上深度可分离卷积代码

文章目录 分组卷积 Group Converlution1、由来和用途2、常规卷积和分组卷积的区别2.1、常规卷积&#xff1a;常规卷积的运算量&#xff1a; 2.2、分组卷积&#xff1a; 3、分组卷积的作用4、深度可分离卷积总结:先做分组卷积&#xff0c;再做1 x 1卷积深度可分离卷积代码 参考博…

【U-Net】训练自己的数据集

代码用的是b导的 整个训练流程也是根据b导的视频来的 源码地址&#xff1a;https://github.com/bubbliiiing/unet-pytorch 博客地址&#xff1a;https://blog.csdn.net/weixin_44791964/article/details/108866828 # 一、准备数据集 1、使用labelme软件标注数据&#xff0c;得…

深度学习 -- 什么是张量 pytorch中张量的几种创建方法

前言 在学习深度学习的过程中&#xff0c;遇到的第一个概念就是张量&#xff0c;张量在pytorch中的计算十分重要&#xff0c;所以本篇博客记录本人学习张量的过程&#xff0c;以及自己的理解。 张量是什么&#xff1f; 张量是一个多维数组&#xff0c;它是标量、向量、矩阵的…

SpringBoot的创建及配置文件

文章目录&#xff1a;一.Spring项目的创建&#xff08;1&#xff09;SpringBoot的含义 &#xff08;2&#xff09;SpringBoot的优点 &#xff08;3&#xff09;项目目录的运行和介绍 二.SpringBoot的配置文件 &#xff08;1&#xff09;配置文件的作用 &#xff08;2&#xff0…

E5--Aurora 8/10Bip核实现光纤接口通信2023-04-22

1.场景 使用两块开发板A和B&#xff0c;通过光纤接口将在A板上ROM中存储的图片数据转发到B板并显示在B板连接的显示屏上&#xff0c;实现光纤接口通信。 具体场景是&#xff0c;由于A735T片上资源有限&#xff0c;因此ROM IP存储了一张1024*600&#xff08;LVDS屏幕&#xff0…

集成光子学在计算领域的机会与挑战【光子学公开课 第133期】

没有听懂&#xff0c;自己浅浅记录一下 背景 深度学习与大模型带来的算力需求&#xff08;需要的算力指数型提高&#xff09; 解决方案 算力池化和算力网络 传统的主机服务&#xff0c;可能会存在闲置资源。 ->改变商业模式&#xff1a;不是出售硬件服务&#xff0c;…

【Makefile通用模板】入门必看篇,超详细

工程目录 假如我们有以下目录结构&#xff1a; . ├── inc │ ├── add.h │ └── sub.h ├── main.c └── src├── add.c└── sub.c文件中的内容如下&#xff1a; //main.c #include <stdio.h> #include "add.h" #include "sub.h&q…

nodejs+vue 智慧餐厅点餐餐桌预订系统

现在社会的生活节奏越来越快&#xff0c;人们对互联网的需求也越来越大&#xff0c;不仅要求使用方便&#xff0c;而且对于功能及扩展性也有了更高的要求&#xff0c;最能达到要求莫过于利用计算机网络&#xff0c;将所需功能要求和计算机网络结合起来&#xff0c;就形成了本智…

FOSSASIA Summit 的参会为 openEuler 全球化注入强心剂

2023年4月15日&#xff0c;亚洲顶级开源盛会FOSSASIA Summit 2023在新加坡落幕。openEuler作为白金赞助级别参会。 自2009年成立以来&#xff0c;除因疫情中断3年之外&#xff0c;FOSSASIA Summit已累计举办11年。作为亚洲年度开源技术旗舰活动&#xff0c;FOSSASIA Summit吸引…

C/C++ 常见编译器说明

文章目录 window下常用的编译器如何获取MSVCMinGW Linux和MaxOS下的编译器gcc和g的区别 window下常用的编译器 window下并不提供原生的类似gcc/g的类unix系统下的C/C编译器。常用的是 MSVC&#xff08;Microsoft Visual C/C&#xff09;编译器&#xff0c;在我们安装完visual …

python 获取脚本所在存储目录

获取.py文件所在目录 问题背景问题原因解决方法 问题背景 项目需要使用到当前脚本所在的目录然后保存文件 我像之前一样&#xff0c;使用了os.getcwd() 去获取脚本目录&#xff0c;保存文件&#xff0c;程序正常运行&#xff0c;但设定路径下没有任何文件&#xff0c;没有头脑…