并查集-- 一种路径压缩实现

2023-10-29

并查集用于计算图连通分量。

比如回答这样的问题:

  • 社交媒体中,用户A和用户B是否属于同一个圈子里的?
  • 一个城市到另一个城市是否是可达的?

并查集适用于并不需要计算出图上具体的路径,只需要计算是否连通。

public interface UnionFindSet {

    // 节点a 和 节点b 是否连通,可达
    public boolean isConnect(int a, int b);

    // 合并节点a和节点b所属群
    public int union(int a,int b);
}

什么是并查集

为了方便理解,数形结合一下:

通常使用数组来标记所有节点所属的群组,如下图所示,parent[i]代表第i个节点的父亲。

初始状态下,每个节点的父亲节点都是自己,代表自己单独一个组:
在这里插入图片描述

分别将1,2,4合并,6,7合并:

在这里插入图片描述
此时,多了2个大小超过1的树。parent[i] = 1代表属于节点1为祖先的群组。判断两个节点是否属于同一群组,只需要判断父亲是否相同即可。

如何合并两个节点呢? 下面给出一个简单实现:

遍历更新

遍历更新的方式,即遍历parent数组将所有属于群组b的节点的父亲设置为群组a的父亲.

public class UnionFind {

     private final int[] parent;


     public UnionFind(int size){
         parent= new int[size];
         for (int i = 0; i < size; i++) {
             parent[i] = i;
         }
     }

     public boolean isConnected(int a, int b){
         return parent[a] == parent[b];
     }

	 // 遍历`parent`数组将所有属于群组b的节点的父亲设置为群组a的父亲.
     public int union(int a, int b){
         if (parent[a] == parent[b]){
             return parent[a];
         }
         for (int i = 0; i < parent.length; i++) {
             if (parent[i] == parent[b]){
                 parent[i] = parent[a];
             }
         }
         return parent[a];
     }
}

这种方式优点是isConnected非常简单,因为树的深度只有一层,只需要一次引用即可。
但是有一个缺陷,即每次合并都要遍历一遍parent数组,其时间复杂度是O(n).

那么为了优化union操作的性能,可以将父子关系设置成一颗树,树可以很好的优化查询性能,合并时只需要将树根指向另一个树根即可。

使用树优化union操作

在这里插入图片描述

上面这棵树,1,2,3,4属于同一群组,6,7属于同一群组。如果将两个群组合并,只需要将节点6的父亲指向节点3.

因为现在群组关系里,树的深度不再为2,因此需要不断向上回溯,找到树根,复杂度为O(h). h为树高。

回溯操作使用一个新函数find来表示。

  • isConnected操作的复杂度为O(h).
  • union操作的复杂度为O(h)

这个实现牺牲了较小的查询性能,换取union操作较大的性能提升。

但是还会出现一个问题,就是二叉树经常出现的不平衡问题。极端情况下,树会变成一个链表,其复杂度还会降低为O(n)。

那么为了解决这个问题,我们可以压缩路径,即减小树高。


public class UnionFind {

     private final int[] parent;


     public UnionFind(int size){
         parent= new int[size];
         for (int i = 0; i < size; i++) {
             parent[i] = i;
         }
     }
     // 找到树根
     public int find(int a){
         if (parent[a]!=a){
             a = parent[a];
         }
         return a;
     }

     public boolean isConnected(int a, int b){
         return find(a) == find(b);
     }

     public int union(int a, int b){
         int ap = find(a);
         int bp = find(b);
         if (ap == bp){
             return ap;
         }
         parent[bp] = ap; // 直接将群组b的树根父亲设置为群组a的树根。
         return ap;
     }

}

路径压缩

路径压缩的方式有很多种,下面给出一种实现:

在find操作的过程中,将所有属于同一群组的节点的父亲,置为树根节点。很方便的使用递归来实现。


public class UnionFind {

	 // 记录节点的父亲节点
     private final int[] parent;


     public UnionFind(int size){
         parent= new int[size];
         for (int i = 0; i < size; i++) {
             parent[i] = i;
         }
     }

	 // 找到节点的祖先节点
     public int find(int a){
         if (parent[a]!= a){
             parent[a] = find(parent[a]); // 将a节点的父亲直接设置成祖先,压缩路径
         }
         return parent[a]; // 递归结束条件为parent[a]== a,即自己是自己的父亲
     }
     // 祖先节点相同的节点属于同一群组
     public boolean isConnected(int a, int b){
         return find(a) == find(b);
     }

	 // 合并两个节点所属群组
     public int union(int a, int b){
         int ap = find(a);
         int bp = find(b);
         // 属于同一群组则返回
         if (ap == bp){
             return ap;
         }
         // 属于不同群组,则将一个祖先的父亲置为另一个
         parent[ap] = bp;
         return ap;
     }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

并查集-- 一种路径压缩实现 的相关文章

  • 查找一个二维矩阵是否是另一个二维矩阵的子集

    最近我参加了一个黑客马拉松 我了解到一个问题 试图在 2d 矩阵中找到网格形式的模式 模式可以是 U H 和 T 并由 3 3 矩阵表示 假设我想展示 H 和 U 1 0 1 1 0 1 1 1 1 gt H 1 0 1 gt U 1 0
  • 基于 2 个输入的伪随机数生成器 [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我需要根据 2 个输入值 X 和 Y 生成一个伪随机数 给定相同的 X 和 Y 值 我需要得到相同的结果 结果应介于 0 和 1 之间 含
  • 如何检测图像是否像素化

    之前有人在 SO 上提出过这样的问题 在Python中检测像素化图像 https stackoverflow com questions 12942365 detecting a pixelated image in python还有关于q
  • 最接近 x,y 的线上的点[重复]

    这个问题在这里已经有答案了 可能的重复 如何判断一个点是否在某条线附近 https stackoverflow com questions 910882 how can i tell if a point is nearby a certa
  • 分组符号最大长度平衡子序列

    将 B 视为分组符号 和 的序列 如果 B 的长度为 0 或 B 具有以下形式之一 则称 B 为平衡序列 X Y 或 X Y 或 X Y 其中 X 和 Y 本身是平衡的 平衡示例 现在的问题是找到一种有效的算法来找到给定输入的最大长度平衡子
  • 从对列表创建邻接列表类型结构

    在 C 中 我有 class Pair int val1 int val2 我有一个来自以下来源的配对列表 List
  • 查找重叠事件/时间的算法

    在处理自定义日历时 我不知道如何找到与任何其他时间段重叠的时间段 时段从 0 点至 720 点 上午 9 点至晚上 9 点 每个像素代表一分钟 var events id 1 start 0 end 40 an event from 9 0
  • 为什么我们需要检测链表中的循环

    我看到很多关于如何检测链表中的循环的问答 但我想了解的是我们为什么要这样做 换句话说 检测链表中的循环的实际用例是什么 在现实生活中 您可能永远不需要检测链表中的循环 但是执行此操作的算法很重要 我在现实生活中多次使用它们 例如 我经常会递
  • O(n^2) 与 O (n(logn)^2)

    时间复杂度是O n 2 or O n logn 2 better 我知道当我们简化它时 它就变成了 O n vs O logn 2 and logn lt n 但是关于logn 2 n is only less than log n 2 f
  • 算法 - 树中所有节点的最大距离

    所以 找到树中两个节点之间的最长路径相当容易 但我想要的是找到从节点出发的最长路径x到树中的另一个节点 对于所有x 这个问题也可以用以下方式表达 计算从给定的树中可以生成的所有有根树的高度 One way of course is to j
  • 为什么使用 no-op 来填补 paxos 事件之间的空白是合法的?

    我正在学习Paxos算法 http research microsoft com en us um people lamport pubs paxos simple pdf http research microsoft com en us
  • 递归:n项级数之和

    需要递归函数 系列是 1 2 3 3 4 5 4 5 6 7 递归求 n 的级数之和 我无法想到应该在函数中传递哪些参数 我的方法 我认为我应该传递 n 要相乘的项数 但我无法想到的是我应该如何在同一个函数中 和 以及我的 return 语
  • 大小为 n 的数组,其中一个元素 n/2 次

    给定一个由 n 个整数组成的数组 其中一个元素出现超过 n 2 次 我们需要在线性时间和恒定的额外空间中找到该元素 YAAQ 又一个数组问题 我有一种偷偷的怀疑 这类似于 在 C 中 We don t need an array publi
  • 让电脑实现360度=0度,旋转炮塔

    我正在制作一个游戏 其中有一个计算机控制的炮塔 炮塔可360度旋转 它使用 trig 找出枪瞄准所需的角度 obj deg 并将枪的当前角度存储在 gun deg 下面的代码以设定的速度旋转枪 if objdeg gt gundeg gun
  • 插入排序 - 如何接受输入并打印排序后的数组

    我试图做一个插入排序程序 它接受任何数据类型 Int Double String 然后打印排序后的数组 我知道我的代码可以工作 但我无法找出真正的问题 import java util public class MyInsertionSor
  • Python 将字符串组合成尽可能短的字符串?

    如果我有一个字符串列表 我想将它们组合成一个具有重叠字符的字符串 如果没有剩余的重叠字符串 请将其添加到末尾 这是一个过于简化的版本 input one two output twone 我正在寻找一种方法来对输入列表中的任意数量的字符串执
  • 为什么 n 按位和 -n 总是返回最右边的位(最后一位)

    这是Python代码片段 1 1 1 2 2 2 3 3 1 看来任何n n总是返回最右边 最后 位 我真的不知道为什么 有人可以帮助我理解这一点吗 这是由于负数以二进制表示的方式 称为二进制补码表示 创建某个数字 n 的补码 换句话说 创
  • 寻找 5 字节 PRNG 的种子

    这是一个古老的想法 但从那时起我就无法找到一些相当好的方法来解决它提出的问题 所以我 发明 了 见下文 一个非常紧凑的 在我看来 性能相当好的 PRNG 但我无法找出算法来为其在大位深度上构建合适的种子值 我当前的解决方案只是暴力破解 它的
  • 为什么对本地列表求和比用“GHC -O2”对教会编码列表求和慢?

    为了测试教会编码的列表如何针对用户定义的列表和本机列表执行 我准备了 3 个基准测试 用户定义的列表 data List a Cons a List a Nil deriving Show lenumTil n go n Nil where
  • Java中获取集合的幂集

    的幂集为 1 2 3 is 2 3 2 3 1 2 1 3 1 2 3 1 假设我有一个Set在爪哇中 Set

随机推荐

  • HMM隐马尔可夫模型进行中文文本分词

    文章目录 一 HMM简述 1 引入 2 隐马尔科夫模型 1 定义 Definition of a hidden Markov model 2 应用 3 前向算法 了解 4 维特比算法 5 前向 后向算法 了解 二 使用HMM进行文本分类 1
  • [Python系列-14]:人工智能 - 数学基础 -4- 数组元素的线性代数运算(向量、矩阵运算)

    作者主页 文火冰糖的硅基工坊 https blog csdn net HiWangWenBing 本文网址 https blog csdn net HiWangWenBing article details 119301224 目录 第1章
  • Configuring App Transport Security Exceptions in iOS 9 and OSX 10.11

    What is App Transport Security ATS At WWDC 2015 Apple announced App Transport Security for iOS 9 and OSX 10 11 El Capita
  • 聊一聊深度学习--包括计算前馈网络的反向传播和卷积的反向传播

    聊一聊深度学习 三天肝完深度学习基础 球球让我过吧 引言 人工智能领域的流派 机器学习流程 了解 表示学习 语义鸿沟 好的数据表示 语义表示 局部表示 分布式表示 学习过程 监督学习 有反馈 无监督学习 无反馈 强化学习 多步之后反馈 神经
  • h5跳转App以及URL Scheme获取-App协议列表

    从Safari跳到APP 跳转 既然要想跳到你指定的APP 那么就需要在你的APP中定义一个特殊的标示 也就是一个URL协议 定义URL协议的如下图 TARGETS gt info gt URL Types gt 添加一个URL协议 Sni
  • Shell脚本攻略:数组

    目录 一 理论 1 数组概述 2 定义数组 3 数组打印 4 数组的数据类型及处理 5 数组赋值 6 数组遍历 7 数组切片 8 数组替换 9 删除数组 10 追加数组中的元素 11 数组排序算法 二 实验 1 实验一 2 实验二 3 实验
  • PS的颜色选择--从英雄到坏蛋的15个配色方案

    从英雄到坏蛋的15个配色方案 故事IGEEK DESIGN 迪斯尼公司于1923成立 他们的第一部长片电影 白雪公主与七个小矮人于1937年发布 关于白雪公主有一个最有趣的地方 她是迪士尼世界中最能代表 善良 的角色 在故事中 邪恶的王后嫉
  • 消息队列mq总结

    转自 http blog csdn net konglongaa article details 52208273 http blog csdn net oMaverick1 article details 51331004 https y
  • Css实现省略号...及悬浮层显示全部内容的方法:

    1 单行文本省略 overflow hidden 溢出隐藏 white space nowrap 禁止换行 text overflow ellipsis 2 多行文本省略 display webkit box 谷歌 webkit box o
  • 软工UML画图

    学习如何画图 如类图 顺序图 流程图 E R图和类代码等 一个一个来 起始 数据流图 功能模型 基本符号 加工 命名要用动宾词组 外部实体 数据存储 数据流 命名要用名词 题目说明解法 下面用个题来说明 假设一家工厂的采购部每天需要一张定货
  • 微服务架构(Microservice Architecture)

    之前一段时间 有听部门架构说起接下来公司要使用微服务架构来研发系统 当时没怎么在意 因为是第一次听说微服务这个名词 果然无知者无畏啊 正好赶上五一假 我自告奋勇的 接了编写微服务架构培训文档这个任务 也许因为我是文科生 文笔稍微好点 五一假
  • Qt-OpenGL-03 纹理Texture

    写在开头 文章是基于纹理 LearnOpenGL CN 教程的学习记录 强烈建议在网站上先弄清楚原理再看此文章 以Qt GL窗口代替GLFW的写法 Qt库中一些类代替教程中的类 一起入坑 效果图 上图使用了两纹理混合 接下来是一些比较重要的
  • LaTeX 中插入图片使其紧跟插入的文字之后

    LaTeX 中插入图片使其不跑到每页的开头而紧跟插入的文字之后 此次建模过程中 遇到的一个比较棘手的问题是 当插入图片时 图片的位置总是会自动跑到当页 或下一页 的最上方 而不是紧跟在其对应的说明文字之后 这是我们想要的效果 解决方法如下
  • 云服务器被DDOS攻击该如何防御?

    相信很多大型网站遭遇到DDoS攻击 导致网站无法访问而又难以解决 包括小编的个人博客也曾接受过DDOS的 洗礼 对此感同身受 所以 本文我们一起来了解下DDOS攻击并分享一些在一定程度范围内的应对方案 关于DDOS攻击 分布式拒绝服务 DD
  • MOS管的原理及其米勒效应(学习笔记)

    一 MOS管的组成及其原理 在讲解MOS的组成之前我们先来了解一下N型半导体和P型半导体 N型半导体是在纯净的硅晶体中掺入了5价磷 此时磷原子最外层多出来了一个自由电子 因为自由电子带负电 所以我们称为N型半导体 N取自于Negative
  • Go语言中json.Marshal()一直返回[123 125]的解决方法

    Go语言中对结构体进行json Marshal 一直返回 123 125 即 原因是go中是否可导出是根据名字首字母是否大写来确定的 如果结构体某字段的首字母为小写则不可导出 例子如下 注意Student内字段首字母的大小写 不可导出 ty
  • Caffe学习3——Forward and Backward

    Forward和Backward是Net中的计算本质 让我们考虑最简单的逻辑回归分类器 前向传播的部分是根据input来计算output 从而进行inference 在前向中 Caffe通过模型中每个layer的计算组合来计算 functi
  • 如何查看 Linux 系统安装的时间

    我们 SUN 实验室每台服务器上架后都需要填写一个表格 这个表格包括详细的机器硬件配置 操作系统版本和安装时间 网络配置 机器名 MAC 地址和 IP 安装的软件和用途 安全级别和策略 联系人 上架时间 机柜号等 昨天有位管理员忘了填写操作
  • Vue中常见设计模式的应用~

    Vue是基于什么模式 表示既然是Vue中常见的设计模式 首当其冲就先聊聊MVVM模式啦 一 mvvm模式 Vue js 是一个基于 MVVM 设计模式的前端框架 它将前端中的 UI视图 与 底层数据 和 业务逻辑 分离开来 使得UI视图与数
  • 并查集-- 一种路径压缩实现

    并查集用于计算图连通分量 比如回答这样的问题 社交媒体中 用户A和用户B是否属于同一个圈子里的 一个城市到另一个城市是否是可达的 并查集适用于并不需要计算出图上具体的路径 只需要计算是否连通 public interface UnionFi