算法.图论-并查集

news2024/9/23 9:51:01

文章目录

    • 1. 并查集介绍
    • 2. 并查集的实现
      • 2.1 实现逻辑
      • 2.2 isSameSet方法
      • 2.3 union方法(小挂大优化)
      • 2.4 find方法(路径压缩优化)
    • 3. 并查集模板
    • 4. 并查集习题
      • 4.1 情侣牵手
      • 4.2 相似字符串组

1. 并查集介绍

定义:
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等
并查集的常见的方法:

方法作用
int find (int)作用就是查找一个元素所在大集合的代表元素, 返回这个元素
boolean isSameSet (int, int)判断传入的两个元素是不是同属一个大集合, 返回T/F
void union (int, int)合并传入的两个元素所代表的大集团(注意不仅仅是这两个元素)

并查集的时间复杂的要求就是实现上述的操作的时间复杂度都是O(1)
下面是关于并查集的一些常见的操作的图示
在这里插入图片描述

2. 并查集的实现

2.1 实现逻辑

不论是哈希表的机构还是list的顺序结构或者是其他的常见的数据结构, 都不可以做到时间复杂度是O(1)的这个指标, 我们直接介绍实现的方式 --> 通过一个father数组以及size数组
关于这两个数组的含义:

数组含义
father下标i代表的是元素的编号, father[i]代表的是他的父亲节点
size下标i代表的是元素的编号, size[i]代表的是这个节点的孩子节点的个数(包括本身)

在这里插入图片描述
初态就是这个样子, 每一个元素的父亲节点都是其本身, 也就是说每一个节点本身就是其所在集合的代表节点, 然后这个集合的大小就是1
下面我们执行操作
step1 : union(a, b)
step2 : union(c, a)
下面是图示(图解一下操作1, 操作2其实是同理的)
在这里插入图片描述
上面的图解也说明了很多问题, 我们的树形结构的挂载的方式是, 小挂大(小的树挂到大树上)
此时进行了union操作之后的逻辑结构就是左下角所示, 此时我们 {a,b} 共属于一个集合, 进行find操作的时候, find(a) 的结果是 b, find(b) 的结果也是 b, 此时size数组中a的值不会再使用了, 因为这时a不可能是领袖节点了, 也就是说这个数据是脏数据…

2.2 isSameSet方法

其实正常来说我们的isSameSet方法和union方法都需要调用find方法, 但是find方法中的路径压缩的技巧是比较重要的, 所以我们单独拎出来放后面说(这里假设已经实现好了), 实现也是比较简单的, 只需要找到这两个元素的代表领袖节点看是不是一个就可以了

	//isSameSet方法
    private static boolean isSameSet(int a, int b){
        return find(a) == find(b);
    }

2.3 union方法(小挂大优化)

解释一下小挂大概念, 在算法导论这本书中说到的是一种秩的概念, 本质上也是为了降低树(集团)的高度所做出的努力, 但这个不是特别必要的…, 也就是在两大集团合并的时候, 小集团(小数目的节点)要依附大集团而存在, 也就是合并的时候, 小集团要挂在大集团上面, 这样可以从一定程度上降低树的高度
代码实现如下

	//union方法
    private static void union(int a, int b){
        int fa = find(a);
        int fb = find(b);
        if(fa != fb){
            sets--;
            if(size[fa] >= size[fb]){
                father[fb] = fa;
                size[fa] += size[fb];
            }else{
                father[fa] = fb;
                size[fb] += size[fa];
            }
        }
    }

2.4 find方法(路径压缩优化)

上面的union的小挂大优化, 其实不是特别必要的, 但是我们find方法中的路径压缩是一定要完成的, 如果没有路径压缩的话, 我们的时间复杂度的指标就不会是O(1)
路径压缩指的就是, 在find方法找到父亲节点的时候, 同时把我们的沿途所有节点的父亲节点都改为找到的父亲节点, 以便于操作的时候不用遍历一个长链去寻找父亲节点, 图解如下
在这里插入图片描述
假设我们执行find(a)操作, 就会如图所示把我们的沿途的所有节点的父亲节点都改为领袖节点e
我们借助的是stack栈结构, 或者是递归(其实就是系统栈)实现的


    private static final int MAX_CP = 31;

    private static final int[] father = new int[MAX_CP];

    private static final int[] size = new int[MAX_CP];

    private static final int[] stack = new int[MAX_CP];
    
	//find方法(路径压缩的迭代实现)
    private static int find1(int a){
        int sz = 0;
        while(father[a] != a){
            stack[sz++] = a;
            a = father[a];
        }

        while(sz > 0){
            father[stack[--sz]] = a;
        }

        return father[a];
    }

	//find方法(路径压缩的递归实现)
    private static int find(int a){
        if(father[a] != a){
            father[a] = find(father[a]);
        }
        return father[a];
    }

3. 并查集模板

上面就是我们关于并查集最基本的分析, 我们提供几个测试链接测试一下

牛客并查集模板

//并查集的基本实现方式
import java.util.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.io.OutputStreamWriter;
import java.io.IOException;

public class Main {
    private static final int MAXN = 1000001;

    private static final int[] father = new int[MAXN];

    private static final int[] size = new int[MAXN];

    private static final int[] stack = new int[MAXN];

    private static int cnt = 0;

    private static void build(int sz) {
        cnt = sz;
        for (int i = 0; i <= cnt; i++) {
            father[i] = i;
            size[i] = 1;
        }
    }

    private static int find(int n) {
        //下面就是扁平化(路径压缩的处理技巧)
        int capacity = 0;
        while (father[n] != n) {
            stack[capacity++] = n;
            n = father[n];
        }

        //开始改变沿途节点的指向
        while (capacity > 0) {
            father[stack[--capacity]] = n;
        }
        return father[n];
    }

    private static boolean isSameSet(int a, int b) {
        return find(a) == find(b);
    }

    private static void union(int a, int b) {
        //下面的设计就是小挂大的思想
        int fa = find(a);
        int fb = find(b);
        if (fa != fb) {
            if (size[fa] >= size[fb]) {
                father[fb] = fa;
                size[fa] += size[fb];
            } else {
                father[fa] = fb;
                size[fb] += size[fa];
            }
        }
    }

    //我们使用的是高效率的io工具(使用的其实就是一种缓存的技术)
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(br);
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        while (in.nextToken() != StreamTokenizer.TT_EOF) {
            int n = (int)in.nval;
            build(n);
            in.nextToken();
            int m = (int)in.nval;
            for (int i = 0; i < m; i++) {
                in.nextToken();
                int op = (int)in.nval;
                in.nextToken();
                int n1 = (int)in.nval;
                in.nextToken();
                int n2 = (int)in.nval;
                if (op == 1) {
                    out.println(isSameSet(n1, n2) ? "Yes" : "No");
                } else {
                    union(n1, n2);
                }
            }
        }
        out.flush();
        out.close();
        br.close();
    }
}

洛谷并查集模板

//并查集的基本实现方式
import java.util.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.io.OutputStreamWriter;
import java.io.IOException;

public class Main {
    private static final int MAXN = 100001;

    private static final int[] father = new int[MAXN];

    private static final int[] size = new int[MAXN];

    private static final int[] stack = new int[MAXN];

    private static int cnt = 0;

    private static void build(int sz){
        cnt = sz;
        for(int i = 0; i <= cnt; i++){
            father[i] = i;
            size[i] = 1;
        }
    }

    private static int find(int n){
        //下面就是扁平化(路径压缩的处理技巧)
        int capacity = 0;
        while(father[n] != n){
            stack[capacity++] = n;
            n = father[n];
        }

        //开始改变沿途节点的指向
        while(capacity > 0){
            father[stack[--capacity]] = n;
        }
        return father[n];
    }

    private static boolean isSameSet(int a, int b){
        return find(a) == find(b);
    }

    private static void union(int a, int b){
        //下面的设计就是小挂大的思想
        int fa = find(a);
        int fb = find(b);
        if(fa != fb){
            if(size[fa] >= size[fb]){
                father[fb] = fa;
                size[fa] += size[fb];
            }else{
                father[fa] = fb;
                size[fb] += size[fa];
            }
        }
    }
    
    //我们使用的是高效率的io工具(使用的其实就是一种缓存的技术)
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(br);
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        while(in.nextToken() != StreamTokenizer.TT_EOF){
            int n = (int)in.nval;
            build(n);
            in.nextToken();
            int m = (int)in.nval;
            for(int i = 0; i < m; i++){
                in.nextToken();
                int op = (int)in.nval;
                in.nextToken();
                int n1 = (int)in.nval;
                in.nextToken();
                int n2 = (int)in.nval;
                if(op == 2){
                    out.println(isSameSet(n1, n2) ? "Y" : "N");
                }else{
                    union(n1, n2);
                }
            }
        }
        out.flush();
        out.close();
        br.close();
    }
}

4. 并查集习题

4.1 情侣牵手

leetcode765.情侣牵手题目链接
在这里插入图片描述

//本题的前置知识可能是置换环(这一题的并查集的思路尤其不好想)
class Solution {
//核心点的分析就是如果一个集合里面有k对情侣, 那么我们至少需要交换 k - 1 次

    private static final int MAX_CP = 31;

    private static final int[] father = new int[MAX_CP];

    private static final int[] size = new int[MAX_CP];

    private static final int[] stack = new int[MAX_CP];

    private static int sets = 0;

    //初始化并查集
    private static void build(int n){
        sets = n;
        for (int i = 0; i < n; i++) {
            father[i] = i;
            size[i] = 1;
        }
    }

    //find方法(路径压缩的实现)
    //find方法(路径压缩的递归实现)
    private static int find(int a){
        if(father[a] != a){
            father[a] = find(father[a]);
        }
        return father[a];
    }

    //isSameSet方法
    private static boolean isSameSet(int a, int b){
        return find(a) == find(b);
    }

    //union方法
    private static void union(int a, int b){
        int fa = find(a);
        int fb = find(b);
        if(fa != fb){
            sets--;
            if(size[fa] >= size[fb]){
                father[fb] = fa;
                size[fa] += size[fb];
            }else{
                father[fa] = fb;
                size[fb] += size[fa];
            }
        }
    }

    public int minSwapsCouples(int[] row) {
        int cpN = row.length / 2;
        build(cpN);
        for(int i = 0; i < row.length; i += 2){
            union(row[i] / 2, row[i + 1] / 2);
        }
        return cpN - sets;
    }
}

4.2 相似字符串组

leetcode839.相似字符串组
在这里插入图片描述

//简单的并查集的应用
class Solution {

    private static final int MAXN = 301;

    private static final int[] father = new int[MAXN];

    private static final int[] size = new int[MAXN];

    private static final int[] stack = new int[MAXN];

    private static int sets = 0;

    //初始化并查集的方式
    private static void build(int n){
        sets = n;
        for(int i = 0; i < n; i++){
            father[i] = i;
            size[i] = 1;
        }
    }

    //find方法
    private static int find(int a){
        int sz = 0;
        while(father[a] != a){
            stack[sz++] = a;
            a = father[a];
        }

        while(sz > 0){
            father[stack[--sz]] = a;
        }

        return father[a];
    }

    //isSameSet方法 
    private static boolean isSameSet(int a, int b){
        return find(a) == find(b);
    }

    //union方法
    private static void union(int a, int b){
        int fa = find(a);
        int fb = find(b);
        if(fa != fb){
            sets--;
            if(size[fa] >= size[fb]){
                size[fa] += size[fb];
                father[fb] = fa;
            }else{
                size[fb] += size[fa];
                father[fa] = fb;
            }
        }
    }

    public int numSimilarGroups(String[] strs) {
        int n = strs.length;
        int m = strs[0].length();
        build(n);
        for(int i = 0; i < n; i++){
            for(int j = i + 1; j < n; j++){
                if (find(i) != find(j)) {
					int diff = 0;
					for (int k = 0; k < m && diff < 3; k++) {
						if (strs[i].charAt(k) != strs[j].charAt(k)) {
							diff++;
						}
					}
					if (diff == 0 || diff == 2) {
						union(i, j);
					}
				}
            }
        }
        return sets;
    }
}

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

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

相关文章

(学习记录)使用 STM32CubeMX——配置时钟(入门)

使用STM32CubeMX配置STM32F103C8T6时钟部分 选择芯片 ①&#xff1a;选择MCU型号 ①&#xff1a;这里使用英文输入法&#xff0c;输入你想要的芯片型号&#xff0c;我这里采用STM32F103C8T6 ②&#xff1a;这里能看到搜索后出来的芯片具体型号&#xff0c;选择匹配度最高的一个…

类和对象(2)(重点)

个人主页&#xff1a;Jason_from_China-CSDN博客 所属栏目&#xff1a;C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目&#xff1a;C知识点的补充_Jason_from_China的博客-CSDN博客 类的默认成员函数 概念概述 默认成员函数就是用户没有显式实现&#xff0c;编译器会自…

【CSS in Depth 2 精译_034】5.4 Grid 网格布局的显式网格与隐式网格(下)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

ACM MM24 | Hi3D: 3D生成领域再突破!新视角生成和高分辨率生成双SOTA(复旦智象等)

文章链接&#xff1a;https://arxiv.org/pdf/2409.07452 Github 链接&#xff1a;https://github.com/yanghb22-fdu/Hi3D-Official 亮点直击 本文提出了高分辨率图像到3D模型&#xff08;Hi3D&#xff09;&#xff0c;这是一种基于视频扩散的新范式&#xff0c;将单个图像重新定…

计算机毕业设计python+spark知识图谱房价预测系统 房源推荐系统 房源数据分析 房源可视化 房源大数据大屏 大数据毕业设计 机器学习

《PythonSpark知识图谱房价预测系统》开题报告 一、研究背景与意义 随着城市化进程的加速和房地产市场的不断发展&#xff0c;房价成为影响人们生活质量的重要因素之一。准确预测房价不仅有助于政府制定科学的房地产政策&#xff0c;还能为开发商提供市场参考&#xff0c;同时…

NLP-transformer学习:(7)evaluate实践

NLP-transformer学习&#xff1a;&#xff08;7&#xff09;evaluate 使用方法 打好基础&#xff0c;为了后面学习走得更远。 本章节是单独的 NLP-transformer学习 章节&#xff0c;主要实践了evaluate。同时&#xff0c;最近将学习代码传到&#xff1a;https://github.com/Mex…

c++类与对象一

C类与对象(一) 面向对象初步认识 在c语言中&#xff0c;编程是面向过程编程&#xff0c;注重求解问题列出过程&#xff0c;然后调用函数求解问题。 在日常生活中。我们经常会遇到面向过程的问题 手洗衣服就是面向过程 而C是基于面向对象的。关注的是对象&#xff0c;把事情…

SpringSecurity -- 入门使用

文章目录 什么是 SpringSesurity &#xff1f;细节使用方法 什么是 SpringSesurity &#xff1f; 在我们的开发中&#xff0c;安全还是有些必要的 用 拦截器 和 过滤器 写代码还是比较麻烦。 SpringSecurity 是 SpringBoot 的底层安全默认选型。一般我们需要认证和授权&#xf…

【Finetune】(三)、transformers之P-Tuning微调

文章目录 0、P-Tuning基本原理1、代码实战1.1、导包1.2、加载数据集1.3、数据集预处理1.4、创建模型1.5、P-tuning*1.5.1、配置文件1.5.2、创建模型 1.6、配置训练参数1.7、创建训练器1.8、模型训练1.9、模型推理 0、P-Tuning基本原理 P-Tuning的基本思想是在prompt-tuning的基…

Spring Boot管理用户数据

目录 学习目标前言Thymeleaf 模板JSON 数据步骤 1: 创建 Spring Boot 项目使用 Spring Initializr 创建项目使用 IDE 创建项目 步骤 2: 添加依赖步骤 3: 创建 Controller步骤 4: 新建index页面步骤 5: 运行应用程序 表单提交步骤 1: 添加 Thymeleaf 依赖在 Maven 中添加依赖 步…

Vue3.3新特性defineModel

defineModel的使用: defineModel选项可以帮我们省去很多麻烦 不仅需要上述操作&#xff0c;还需要进行一定的配置&#xff1a; 在vite.config.js中进行配置 defineModel是一个宏&#xff0c;所以不需要从vue中import导入&#xff0c;直接使用就可以了。这个宏可以用来声明一个…

Java之线程篇六

目录 CAS CAS伪代码 CAS的应用 实现原子类 实现自旋锁 CAS的ABA问题 ABA问题导致BUG的例子 相关面试题 synchronized原理 synchronized特性 加锁过程 相关面试题 Callable 相关面试题 JUC的常见类 ReentrantLock ReentrantLock 和 synchronized 的区别: 原…

Android 新增目录怎么加入git

工作中会遇到android系统源码系统增加第三方功能支持&#xff0c;需要增加目录&#xff0c;那么这个目录怎么提交到服务器上去呢&#xff1f;接下来我们就看下这个问题的解决 一服务器创建仓库 一个新的目录增加到仓库中&#xff0c;需要仓库有对应的仓库地址&#xff0c;这个让…

小记编程语言浮点精度问题

注意&#xff1a; 本文内容于 2024-09-15 20:21:12 创建&#xff0c;可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容&#xff0c;请访问原文地址&#xff1a;小记编程语言浮点精度问题。感谢您的关注与支持&#xff01; 浮点数在计算机中不能精确表示所有…

docker启动mysql未读取my.cnf配置文件问题

描述 在做mysql主从复制配置两台mysql时&#xff0c;从节点的my.cnf配置为&#xff1a; [mysqld] datadir /usr/local/mysql/slave1/data character-set-server utf8 lower-case-table-names 1 # 主从复制-从机配置# 从服务器唯一 ID server-id 2 # 启用中继日志 relay-l…

【小沐学CAD】3ds Max常见操作汇总

文章目录 1、简介2、二次开发2.1 C 和 3ds Max C SDK2.2 NET 和 3ds Max .NET API2.3 3ds Max 中的 Python 脚本2.4 3ds Max 中的 MAXScript 脚本 3、快捷键3.1 3Dmax键快捷键命令——按字母排序3.2 3dmax快捷键命令——数字键3.3 3dmax功能键快捷键命令3.4 3Dmax常用快捷键——…

对网页聊天项目进行性能测试, 使用JMeter对于基于WebSocket开发的webChat项目的聊天功能进行测试

登录功能 包括接口的设置和csv文件配置 ​​​​​​ 这里csv文件就是使用xlsx保存数据, 然后在浏览器找个网址转成csv文件 注册功能 这里因为需要每次注册的账号不能相同, 所以用了时间函数来当用户名, 保证尽可能的给正确的注册数据, 时间函数使用方法如下 这里输入分钟, 秒…

肝内胆管癌中三级淋巴结构分布与临床预后的相关性研究|文献精析·24-09-22

小罗碎碎念 这篇文章是关于肝内胆管癌&#xff08;intrahepatic cholangiocarcinoma, iCCA&#xff09;中三级淋巴结构&#xff08;tertiary lymphoid structures, TLSs&#xff09;的分布、密度及其对临床结果的预测价值的研究。 作者类型作者姓名单位名称&#xff08;中文&a…

数据结构——串的模式匹配算法(BF算法和KMP算法)

算法目的&#xff1a; 确定主串中所含子串&#xff08;模式串&#xff09;第一次出现的位置&#xff08;定位&#xff09; 算法应用&#xff1a; 搜索引擎、拼写检查、语言翻译、数据压缩 算法种类&#xff1a; BF算法&#xff08;Brute-Force&#xff0c;又称古典的…

【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解

【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解 题目传送门 题解 CSP-S1 补全程序&#xff0c;致敬全 A 的答案&#xff0c;和神奇的预言家。 写一下这篇的题解说不定能加 CSP 2024 的 RP 首先看到 k k k 这么大的一个常数&#xff0c;就想到了二分。然后写一个判…