并查集算法

目录

1.算法介绍

1.1什么是并查集呢,它又是用来干什么的呢?

1.2问题引入

 2.算法解析

2.1初始化

2.2合并操作

2.3查找 + 路径压缩 

 2.4问题解决代码

3.变式突破


1.算法介绍

1.1什么是并查集呢,它又是用来干什么的呢?

逐字拆解一下,并、查、集。这个三个字,其中前面两个字都是动词,第三个字是个名词。

(1)并查集可以进行集合合并的操作(并)
(2)并查集可以查找元素在哪个集合中(查)
(3)并查集维护的是一堆集合(集)

基本原理:每个集合用一棵树来表示。树的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点

 并查集算法无论是集合合并还是元素查询,时间复杂度都是接近  0(1)

1.2问题引入

模板题:

一共有 n 个数,编号是 1∼n1,最开始每个数各自在一个集合中。

现在要进行 m个操作,操作共有两种:

  1. M a b,将编号为 a和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
  2. Q a b,询问编号为 a和 b的两个数是否在同一个集合中;

输入格式

第一行输入整数 n和 m。

接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。

输出格式

对于每个询问指令 Q a b,都要输出一个结果,如果 a和 b在同一集合内,则输出 Yes,否则输出 No

每个结果占一行。

数据范围

1≤n,m≤10^5


 2.算法解析

一些说明:

1.假如 n==5,就是说有5个元素1~5,分别所属在集合1到5中。

2.p数组存集合的编号,表示是哪个集合。

3.查询元素在哪个集合的函数是find(int x),会返回该元素所在的集合编号。

学习并查集模板,其实学习的就是下面三步 

2.1初始化

做法:用一个数组保存对应位置元素所属集合的编号。

模板代码:

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

很容易理解,就是将当前元素的父节点指向自己 

此时p[1]==1,就表示元素1在集合1中。  

用n==5举例,此时代码表示的集合的状态:

2.2合并操作

做法:若想A,B两个集合合并:可以将B集合的集合编号设置为A集合的集合编号。

 模板代码:

p[find(B)]=find(A)

此时代码表示的集合的状态: 

 

2.3查找 + 路径压缩 

正是因为进行了路径压缩, 并查集的时间复杂度接近 0(1)。

先看模板代码:

 public static int find(int x){ //返回x所在的集合编号 + 路径压缩
        //祖先节点的父节点是自己本身
        if(p[x] != x){
            //将x的父亲置为x父亲的祖先节点,实现路径的压缩
            p[x] = find(p[x]);
        }
        return p[x];
    }

路径压缩的最终目的:让一个集合中的任意元素(用x表示)的p[x]里都存储其集合编号。

此时的集合状态: 

我们从递归和回溯两方面分析:

最终 集合的状态

 2.4问题解决代码

import java.util.Scanner;

public class Main {
    /*一共有 n个数,编号是 1∼n,最开始每个数各自在一个集合中。现在要进行 m
        个操作,操作共有两种:M a b,将编号为 a和 b
        的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
        Q a b,询问编号为 a和 b的两个数是否在同一个集合中;*/
    /*输入格式
    第一行输入整数 n和 m。
    接下来 m行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。*/
    static int N = 100010;
    static int[] p = new int[N];

    //并查集的核心操作,寻找根节点祖先 + 路径压缩
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int q = in.nextInt();
        //初始化
        for (int i = 1; i <= n; i++) {
            p[i] = i;
        }
        while (q-- > 0) {
            String s = in.next();
            int a = in.nextInt();
            int b = in.nextInt();
            //合并操作
            if (s.equals("M")) {
                p[find(a)] = find(b);
            } else if (s.equals("Q")) {
                //查找
                if (find(a) == find(b)) {
                    System.out.println("Yes");
                } else {
                    System.out.println("No");
                }
            }
        }
    }

    public static int find(int x){ //返回x所在的集合编号 + 路径压缩
        //祖先节点的父节点是自己本身
        if(p[x] != x){
            //将x的父亲置为x父亲的祖先节点,实现路径的压缩
            p[x] = find(p[x]);
        }
        return p[x];
    }
}

3.变式突破

题目:

给定一个包含 n 个点(编号为 1∼n)的无向图,初始时图中没有边。

现在要进行 m个操作,操作共有三种:

  1. C a b,在点 a和点 b 之间连一条边,a和 b 可能相等;
  2. Q1 a b,询问点 a和点 b 是否在同一个连通块中,a和 b 可能相等;
  3. Q2 a,询问点 a 所在连通块中点的数量;

输入格式

第一行输入整数 n和 m。

接下来 m 行,每行包含一个操作指令,指令为 C a bQ1 a b 或 Q2 a 中的一种。

输出格式

对于每个询问指令 Q1 a b,如果 a 和 b在同一个连通块中,则输出 Yes,否则输出 No

对于每个询问指令 Q2 a,输出一个整数表示点 a 所在连通块中点的数量

每个结果占一行。

数据范围

1≤n,m≤10^5

与模板题不同处的说明:

与模板题唯一不同的是,我们要多维护数组size来记录该元素的集合中元素的个数。

1.对于数组size只保证根节点的size有意义。

2.初始化时,还要初始化每个集合内的元素数量为1。

初始化的代码:

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

3.合并a和b所在的两个集合
合并集合操作同时要记得维护集合大小关系

比如集合a合并到集合b里的代码:

p[find(a)] = find(b);
    size[b] += size[a];

代码:

import java.util.Scanner;

public class Main {
    /* 现在要进行 m个操作,操作共有三种:
     C a b,在点 a和点 b之间连一条边,a和 b可能相等;
     Q1 a b,询问点 a和点 b是否在同一个连通块中,a
     和 b可能相等;Q2 a,询问点 a所在连通块中点的数量;*/
   /* 输入格式
    第一行输入整数 n和 m。
    接下来 m行,每行包含一个操作指令,指令为 C a b,Q1 a b 或 Q2 a 中的一种。
*/
    /*输出格式
    对于每个询问指令 Q1 a b,如果 a和 b在同一个连通块中,则输出 Yes,否则输出 No。
    对于每个询问指令 Q2 a,输出一个整数表示点 a所在连通块中点的数量 每个结果占一行。*/
    static int N = 100010;
    static int[] p = new int[N];
    static int[] size = new int[N];

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        for (int i = 1; i <= n; i++) {
            p[i] = i;
            size[i] = 1;
        }
        while (m-- > 0) {
            String s = scanner.next();
            if (s.equals("C")) {
                int a = scanner.nextInt();
                int b = scanner.nextInt();
                if (find(a) != find(b)) {
                    size[find(b)] += size[find(a)];
                    p[find(a)] = find(b);
                }
            } else if (s.equals("Q1")) {
                int a = scanner.nextInt();
                int b = scanner.nextInt();
                if (find(a) == find(b)) {
                    System.out.println("Yes");
                } else {
                    System.out.println("No");
                }
            } else {
                int a = scanner.nextInt();
                System.out.println(size[find(a)]);
            }
        }
    }

    private static int find(int x) {
        if (x != p[x]) {
            p[x] = find(p[x]);
        }
        return p[x];
    }
}

以上为我个人的小分享,如有问题,欢迎讨论!!! 

都看到这了,不如关注一下,给个免费的赞 

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

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

相关文章

经典的泡泡龙游戏源码免费下载

源码介绍 HTML5泡泡龙冒险小游戏是一款休闲网页游戏&#xff0c;游戏玩法是玩家从下方中央的弹珠发射台射出彩珠&#xff0c;多于3个同色珠相连则会消失。 源码下载 经典的泡泡龙游戏源码免费下载

【Python绘画】画正方形简笔画

本文收录于 《一起学Python趣味编程》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、代码示例三、知识点梳理四、总结 一、前言 本文介绍如何使用Python的海龟画图工具turtle&#…

docker-compose入门级实战教程

&#x1f31f;&#x1f30c; 欢迎来到知识与创意的殿堂 — 远见阁小民的世界&#xff01;&#x1f680; &#x1f31f;&#x1f9ed; 在这里&#xff0c;我们一起探索技术的奥秘&#xff0c;一起在知识的海洋中遨游。 &#x1f31f;&#x1f9ed; 在这里&#xff0c;每个错误都…

Flutter Bloc之简单记录

目录 0.库安装 1.插件和自动生成 2.状态的配置 1.初始化中&#xff1a; 2.赋值完成后&#xff1a; 3.如果出错&#xff1a; 3.事件的配置 1.定义一个读取事件 2.定义一个更改事件 4.Bloc的设置 5.Bloc的使用 1.BlocProvider 2.内部调用 参考文章进行类的配置 0.库…

【ARM Cache 系列文章 2.1 -- Cache PoP 及 PoDP 介绍】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 PoP 及 PoDPCache PoDPCache PoP应用和影响PoP 及 PoDP Cache PoDP 点对深度持久性(Point of Deep Persistence, PoDP)是内存系统中的一个点,在该点达到的任何写操作即使在系统供电…

跳跃游戏二

方法一&#xff1a;&#xff08;双指针法&#xff09;此题参考跳台阶问题&#xff0c;题目要求求到达最后一个点的最小跳跃次数&#xff0c;那么我们就可以从最后一个往前推&#xff0c;先看谁能离得最远&#xff0c;并且能跳到最后一个。假设i位置是离最后一个位置最远&#x…

网工内推 | 联通公司,云计算售前,AWS认证优先

01 联通数字科技有限公司 &#x1f537;招聘岗位&#xff1a;云计算售前工程师 &#x1f537;职责描述&#xff1a; 1.了解私有云&#xff0c;公有云&#xff0c;混合云等云计算技术知识&#xff0c;了解云计算行业现状及发展趋势。 2.承担区域项目售前工作支持&#xff0c;为…

Linux基础指令磁盘管理002

LVM&#xff08;Logical Volume Manager&#xff09;是Linux系统中一种灵活的磁盘管理和存储解决方案&#xff0c;它允许用户在物理卷&#xff08;Physical Volumes, PV&#xff09;上创建卷组&#xff08;Volume Groups, VG&#xff09;&#xff0c;然后在卷组上创建逻辑卷&am…

NSS题目练习7

[MoeCTF 2022]baby_file 打开看见一串源代码&#xff0c;需要get传参传入file 题目提示php伪协议 用dirsearch扫描发现flag.php 用php伪协议查看&#xff0c;回显一串base64编码 解码后得到flag [鹤城杯 2021]Middle magic 读取这两个文件 一个php正则表达式 补充&#xff1a…

Element ui图片上传

前言 对于广大小白来说&#xff0c;图片上传简直是上传难&#xff0c;难于上青天&#xff01;废话不多说&#xff0c;步入正题&#xff0c;您就瞧好吧&#xff01; 步骤一&#xff1a;前端使用element ui组件&#xff08;upload上传&#xff09; 我个人喜欢使用第二个组件&a…

Python 实现乘数加密法

乘数加密是简单代替密码的一种。乘数加密法脱胎于凯撒加密法,加密和解密符号设计把他们转换成数字,加上或者减去密钥,然后把新的数字转换回符号,当我们把加减密钥变成乘以密钥,就是乘法加密法。有关凯撒加密法可以看之前的文章《Python实现凯撒加解密》。 加密过程 乘数加…

【宠粉赠书】大模型时代的网络安全:安恒“网安三剑客”实战指南

不知不觉中&#xff0c;小智的粉丝已经突破一万。为了回馈粉丝们的厚爱&#xff0c;今天小智给大家送上一套网络安全界的三宝书——安恒"网安三剑客"。下面我会详细给大家介绍这套图书&#xff0c;文末留有领取方式。 随着人工智能&#xff08;AI&#xff09;和大模型…

HarmonyOS(二十五)——Harmonyos通用事件之点击事件

组件被点击时触发的事件就是点击事件。 1.事件 名称支持冒泡功能描述onClick(event: (event?: ClickEvent) > void)否点击动作触发该回调&#xff0c;event返回值见ClickEvent对象说明。从API version 9开始&#xff0c;该接口支持在ArkTS卡片中使用。 2.ClickEvent对象…

Etcd Raft架构设计和源码剖析1:宏观架构

Etcd Raft架构设计和源码剖析1&#xff1a;宏观架构 | Go语言充电站 序言 Etcd提供了一个样例contrib/raftexample&#xff0c;用来展示如何使用etcd raft。这篇文章通过raftexample介绍如何使用etcd raft。 raft服务 raftexample是一个分布式KV数据库&#xff0c;客户端可…

MySQL数据库常见工具的基础使用_1

在上一篇文章中提到了对MySQL数据库进行操作的一些常见工具 mysqlcheck mysqlcheck是一个用于数据库表的检查&#xff0c;修复&#xff0c;分析和优化的一个客户端程序 分析的作用是查看表的关键字分布,能够让sql生成正确的执行计划(支持InnoDB,MyISAM,NDB)检查的作用是检查…

BGP基础配置实验

接下来R1&#xff0c;R2就可以通过三次握手建立TCP会话 然后建立R2&#xff0c;R4邻居 对R5和R4 然后拿4ping5 但是在4&#xff0c;5之间不能跑别的协议&#xff0c;然后要直接告知才可以 再ping通了建立邻居 用环回建邻居要改一下原 然后在建立的时候要把R4TTL值改成2&#xf…

机器学习:更多关于元学习

目录 Meta Learning vs Self-supervised Learning 自监督学习——找初始化的参数MAML 自动学出合适的参数 MAML&#xff1a;不断的学初始化参数MAML的初始化参数来自BERT MAML&#xff1a;找出来的初始化参数能在训练任务上表现的很好BERT&#xff1a;自监督目标是不同的下游任…

门外汉一次过软考中级(系统集成项目管理工程师)秘笈,请收藏!

24上软考考试已经结束&#xff0c;24下软考备考又要开启了&#xff01;今年软考发生了改革&#xff0c;很多考试由一年考两次变成了一年考一次&#xff0c;比如高级信息系统项目管理师&#xff0c;比如中级系统集成项目管理工程师&#xff0c;这两科是高、中级里相对简单&#…

GLM-4-9B性能究竟如何?

GLM-4-9B 开源系列模型 前言 自 2023 年 3 月 14 日 ChatGLM-6B 开源以来&#xff0c;GLM 系列模型受到广泛认可。特别是在 ChatGLM3-6B 开源后&#xff0c;针对让小模型能够拥有更为强大的能力这一目标&#xff0c;GLM 技术团队展开了诸多的探索性工作。历经将近半年的探索历程…

springboot 打成jar部署到Linux环境后读取resources下面的文件

方法代码&#xff1a; ClassLoader loader Thread.currentThread().getContextClassLoader();InputStream flagInputStream loader.getResourceAsStream("static/imagesLogo/imageaaa.png");BufferedImage read;read ImageIO.read(flagInputStream);System.out.pr…