并查集快速查找,快速合并

并查集基础

一、概念及其介绍

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。

并查集的思想是用一个数组表示了整片森林(parent),树的根节点唯一标识了一个集合,我们只要找到了某个元素的的树根,就能确定它在哪个集合里。

二、适用说明

并查集用在一些有 N 个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这个过程看似并不复杂,但数据量极大,若用其他的数据结构来描述的话,往往在空间上过大,计算机无法承受,也无法在短时间内计算出结果,所以只能用并查集来处理。

三、并查集的基本数据表示

并查集快速查找,快速合并

如上图 0-4 下面都是 05-9 下面都是 1,表示 0、1、2、3、4 这五个元素是相连接的, 5、6、7、8、9 这五个元素是相连的。

并查集快速查找,快速合并

再如上图 0、2、4、6、8 下面都是 0 这个集合,表示 0、2、4、6、8 这五个元素是相连接的, 1、3、5、7、9 下面都是 1 这个集合,表示 0,1、3、5、7、9 这五个元素是相连的。

构造一个类 UnionFind,初始化, 每一个id[i]指向自己, 没有合并的元素:

public UnionFind1(int n) {
count = n;
id = new int[n];
// 初始化, 每一个id[i]指向自己, 没有合并的元素
for (int i = 0; i < n; i++)
id[i] = i;
}

Java 实例代码

UnionFond.java

public class UnionFind{
    private int[] id;
    // 数据个数
    private int count;

    public UnionFind1(int n) {
        count = n;
        id = new int[n];
        for (int i = 0; i < n; i++)
            id[i] = i;
    }

}

并查集快速查找

本小节基于上一小节并查集的结构介绍基础操作,查询和合并和判断是否连接。

查询元素所在的集合编号,直接返回 id 数组值, O(1) 的时间复杂度。

private int find(int p) {
assert p >= 0 && p < count;
return id[p];
}

合并元素 p 和元素 q 所属的集合, 合并过程需要遍历一遍所有元素, 再将两个元素的所属集合编号合并,这个过程是 O(n) 复杂度。

public void unionElements(int p, int q) {
int pID = find(p);
int qID = find(q);
if (pID == qID)
return;
for (int i = 0; i < count; i++)
if (id[i] == pID)
id[i] = qID;
}

Java 实例代码

UnionFond1.java


/**
 * 第一版union-Find
 */
public class UnionFind1 {
    // 我们的第一版Union-Find本质就是一个数组
    private int[] id;
    // 数据个数
    private int count;

    public UnionFind1(int n) {
        count = n;
        id = new int[n];
        // 初始化, 每一个id[i]指向自己, 没有合并的元素
        for (int i = 0; i < n; i++)
            id[i] = i;
    }

    // 查找过程, 查找元素p所对应的集合编号
    private int find(int p) {
        assert p >= 0 && p < count;
        return id[p];
    }

    // 查看元素p和元素q是否所属一个集合
    // O(1)复杂度
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    // 合并元素p和元素q所属的集合
    // O(n) 复杂度
    public void unionElements(int p, int q) {

        int pID = find(p);
        int qID = find(q);

        if (pID == qID)
            return;

        // 合并过程需要遍历一遍所有元素, 将两个元素的所属集合编号合并
        for (int i = 0; i < count; i++)
            if (id[i] == pID)
                id[i] = qID;
    }
}

并查集快速合并

对于一组数据,并查集主要支持两个动作:

  • union(p,q) – 将 p 和 q 两个元素连接起来。
  • find(p) – 查询 p 元素在哪个集合中。
  • isConnected(p,q) – 查看 p 和 q 两个元素是否相连接在一起。

在上一小节中,我们用 id 数组的形式表示并查集,实际操作过程中查找的时间复杂度为 O(1),但连接效率并不高。

本小节,我们将用另外一种方式实现并查集。把每一个元素,看做是一个节点并且指向自己的父节点,根节点指向自己。如下图所示,节点 3 指向节点 2,代表 3 和 2 是连接在一起的,节点2本身是根节点,所以指向自己。

并查集快速查找,快速合并

同样用数组表示并查集,但是下面一组元素用 parent 表示当前元素指向的父节点,每个元素指向自己,都是独立的。

并查集快速查找,快速合并

并查集快速查找,快速合并

如果此时操作 union(4,3),将元素 4 指向元素 3:

并查集快速查找,快速合并

数组也进行相应改变:

并查集快速查找,快速合并

判断两个元素是否连接,只需要判断根节点是否相同即可。

如下图,节点 4 和节点 9 的根节点都是 8,所以它们是相连的。

并查集快速查找,快速合并

连接两个元素,只需要找到它们对应的根节点,使根节点相连,那它们就是相连的节点。

假设要使上图中的 6 和 4 相连,只需要把 6 的根节点 5 指向 4 的根节点 8 即可。

并查集快速查找,快速合并

构建这种指向父节点的树形结构, 使用一个数组构建一棵指向父节点的树,parent[i] 表示 i 元素所指向的父节点。

private int[] parent;
private int count; // 数据个数

查找过程, 查找元素 p 所对应的集合编号,不断去查询自己的父亲节点, 直到到达根节点,根节点的特点 parent[p] == p,O(h) 复杂度, h 为树的高度。

private int find(int p){
assert( p >= 0 && p < count );
while( p != parent[p] )
p = parent[p];
return p;
}

合并元素 p 和元素 q 所属的集合,分别查询两个元素的根节点,使其中一个根节点指向另外一个根节点,两个集合就合并了。这个操作是 O(h) 的时间复杂度,h 为树的高度。

public void unionElements(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if( pRoot == qRoot )
return;
parent[pRoot] = qRoot;
}

Java 实例代码

UnionFond2.java

/**
 * 第二版unionFind
 */
public class UnionFind2 {
    // 我们的第二版Union-Find, 使用一个数组构建一棵指向父节点的树
    // parent[i]表示第一个元素所指向的父节点
    private int[] parent;
    private int count;  // 数据个数
    // 构造函数
    public UnionFind2(int count){
        parent = new int[count];
        this.count = count;
        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for( int i = 0 ; i < count ; i ++ )
            parent[i] = i;
    }
    // 查找过程, 查找元素p所对应的集合编号
    // O(h)复杂度, h为树的高度
    private int find(int p){
        assert( p >= 0 && p < count );
        // 不断去查询自己的父亲节点, 直到到达根节点
        // 根节点的特点: parent[p] == p
        while( p != parent[p] )
            p = parent[p];
        return p;
    }
    // 查看元素p和元素q是否所属一个集合
    // O(h)复杂度, h为树的高度
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }
    // 合并元素p和元素q所属的集合
    // O(h)复杂度, h为树的高度
    public void unionElements(int p, int q){
        int pRoot = find(p);
        int qRoot = find(q);
        if( pRoot == qRoot )
            return;
        parent[pRoot] = qRoot;
    }
}

Original: https://www.cnblogs.com/siwuxiebuff/p/16208534.html
Author: 思无邪buff
Title: 并查集快速查找,快速合并

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570249/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 从零开始实现放置游戏(十五)——实现战斗挂机(6)在线打怪练级

    本章初步实现游戏的核心功能——战斗逻辑。 战斗系统牵涉的范围非常广,比如前期人物的属性、怪物的配置等,都是在为战斗做铺垫。 战斗中,人物可以施放魔法、技能,需要技能系统支持。 战斗…

    Java 2023年6月5日
    080
  • 面试官:你说说一条查询SQL的执行过程

    为了理解这个问题,先从Mysql的架构说起,对于Mysql来说,大致可以分为3层架构。 第一层作为客户端和服务端的连接,连接器负责处理和客户端的连接,还有一些权限认证之类。比如客户…

    Java 2023年6月13日
    082
  • SpringBoot整合Redis

    创建redis缓存配置类,配置插件(较为固定) package com.xsha.servicebase; import com.fasterxml.jackson.annotat…

    Java 2023年6月13日
    071
  • JVM常用调优配置参数

    常用的JVM配置参数: -Xms2g:初始化堆大小为 2g; -Xmx2g:堆最大内存为 2g; -XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4; -XX:S…

    Java 2023年6月8日
    0120
  • spring guides

    https://spring.io/guides Original: https://www.cnblogs.com/WCFGROUP/p/11621728.htmlAuthor:…

    Java 2023年5月30日
    088
  • Spring Data JPA系列4——Spring声明式事务处理与多数据源支持

    大家好,又见面了。 到这里呢,已经是本 SpringData JPA系列文档的第四篇了,先来回顾下前面三篇: 在第1篇《Spring Data JPA系列1:JDBC、ORM、JP…

    Java 2023年6月7日
    095
  • 【莫傷曉_开发笔记】linux java绘图字体乱码问题

    如题,引起这个问题的主要原因是因为现在一般的云服务器(Linux)的字体库只有默认的英文字体,但是Java绘图时常常要添加一些例如宋体,黑体,微软雅黑之类的字体,字体库中找不到相应…

    Java 2023年6月15日
    060
  • Java 基础【19】代理

    Java 代理(Proxy)模式与现实中的代理含义一致,如旅游代理、明星的经纪人。 在目标对象实现基础上,增加额外的功能操作,由此来扩展目标对象的功能。 JavaWeb 中最常见的…

    Java 2023年5月29日
    0101
  • 【微服务】:何为微服务、网关、服务发现/注册?

    【微服务】:何为微服务、网关、服务发现/注册? 随着互联网业务复杂性慢慢提高,单机服务的架构慢慢出现了生产效率问题 微服务架构带来的有优点也有缺点,使用前需要调研清楚 微服务架构的…

    Java 2023年6月5日
    0104
  • git安装与使用,未完待续… …

    一、git概念 二、git简史 三、git的安装 四、git结构 五、代码托管中心—本地库和远程库的交互方式 六、初始化本地仓库 七、git常用命令 1、add和commit命令 …

    Java 2023年6月5日
    0102
  • Spring Ioc源码分析系列–前言

    Spring Ioc源码分析系列–前言 为什么要写这个系列文章 首先这是我个人很久之前的一个计划,拖了很久没有实施,现在算是填坑了。其次,作为一个Java开发者,Spr…

    Java 2023年6月8日
    085
  • Java Reference 原理

    注意,以下所述源码版本为 JDK 1.8.0_212 1 引用的概念 Java中的数据类型分为: 基本数据类型:byte、short、int、long、float、double、c…

    Java 2023年5月29日
    090
  • AOP spring boot 使用AOP面向切面编程

    关于AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充…

    Java 2023年6月5日
    083
  • 面经

    本文收集最近遇到的好问题,保持学习,保持进步。 数据结构 树的深度优先和广度优先的算法实现 数据库的联合索引的底层实现 map 如何保证顺序 谈谈 HashMap 的源码 谈谈如何…

    Java 2023年6月8日
    089
  • jQuery之过滤选择器

    过滤选择器 1.基本选择器 2.内容选择器 3.可见性选择器 4.子元素过滤器 过滤选择器简称:过滤器。它其实也是一种选择器,而这种选择器类似于CSS3(http://t.mb5u…

    Java 2023年6月6日
    0101
  • Spring Cloud 还没学明白,Istio 又是什么鬼??

    背景 过去,我们运维着”能做一切”的大型单体应用程序。这是一种将产品推向市场的很好的方式,因为刚开始我们也只需要让我们的第一个应用上线。 而且我们总是可以回…

    Java 2023年5月29日
    099
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球