union-find(代码片段)

archeroc archeroc     2022-11-17     296

关键词:

并查集

前言

来自知乎,Coursera 上普林斯顿大学的算法公开课,稍微来博客上写写记记。

课程资源:1. Algorithms, Part I 2. Algorithms, Part II 3. Algorithms, 4th Edition


Dynamic Connectivity

动态连通性问题,这里的连通是一个等价关系,满足:

  1. symmetric: 自反性, p 和 p 自身是连通的。

  2. transitive: 传递性,如果 p 和 q 连通,又有 q 和 r 连通,那么 p 和 r 连通。

  3. reflexive: 对称性, p 和 q 连通,则 q 和 p 连通。

目标是设计一个高效的数据结构,支持大规模的对象集合,支持频繁的合并和查找操作。

连通分量

API

API

Dynamic Connectivity Client

client-result

注:想在自己电脑上跑跑的话, algs4.jar ,测试数据在 booksite 上都有。

Quick Find

当且仅当 id[p] 和 id[q] 相等时,p 和 q 才属于同一个连通分量。

quick-find id

查找操作就只要判断 id 是否相等即可,合并则需要把和 id[p] 相等的所有 id 都改成 id[q] 。

quick-find overview

官方示例:QuickFindUF.java

合并操作

public void union(int p, int q) 
    int pid = id[p];
    int qid = id[q];
    for (int i = 0; i < id.length; i++) 
        if (id[i] == pid) 
            id[i] = qid;
        
    

其中的 for 循环写成下面这样是错的。

for (int i = 0; i < id.length; i++) 
    if (id[i] == id[p]) 
        id[i] = id[q];
    

id[p] 在循环中变成了 id[q] ,原来相等的关系变成不等,导致数组中排在其后面的本应改变的对象无法更新 id 。举例来说,上面那张图要是这样合并 5 和 9 的话, 6 与 7 的 id 就还会是 1 , 而不会更新成 8 。

这种实现,查找操作很快,但对 n 个对象进行 n 次合并需要访问数组 n^2 次,平方级别对大规模的数据来说是不可接受的。

Quick Union

将连通分量抽象成树, id[p] 表示 p 的父节点。

quic-union id

查找操作要检查 p q 是否有相同的根节点,合并操作则只要把 p 根节点的父节点改成 q 的根节点即可,只改变了 id[] 中的一个值。

quick-union overview

官方示例:QuickUnionUF.java

找根节点

private int root(int i) 
    while (i != id[i]) 
        i = id[i];
    

    return i;

这样查找和合并的实现都可以写得和简洁,就上面的 while() 需要考虑下。

因为树有可能很高,找根节点就需要访问很多次数组,查找和合并操作都不快。

Improvements

在 quick union 基础上加以改进。

Weighted Quick Union

合并时加以一定约束:保证是将小树合并到大树上,来避免出现过高的树。

weighted-quick-union-overview

这样一来任意的节点 x 的深度最多为 lgN (以 2 为底),N 为 100 w 时深度最多是 20, 10 亿时是 30 ,相对来说可以支持较大规模的数据了。

至于为什么是 lgN ,可以粗略的这么想:节点 x 的深度只有在其所在的树 T1 被合并到另一个更大的树 T2 时才会加一,而 size(T2) >= size(T1),那么节点 x 所在的树的大小至少会变成两倍。而总共 N 个节点,最多可以两倍 lgN 次,深度加一 lgN 次,即深度最多为 lgN 。

weighted-quick-union

官方示例:WeightedQuickUnionUF.java

合并

public void union(int p, int q) 
    int i = root(p);
    int j = root(q);
    if (i == j) 
        return;
    
    if (sz[i] < sz[j]) 
        id[i] = j;
        sz[j] += sz[i];
    
    else 
        id[j] = i;
        sz[i] += sz[j];
    

Path Compression

此外还可以加上路径压缩,来进一步改善性能。所谓路径压缩,就是在找到根节点之后,把经过的点都直接连到根节点上,降低树高。

举例来说,下面合并 5 和 9 。

weighted-quick-union-with-path-compression

录自 visualgo

官方示例:WeightedQuickUnionPathCompressionUF.java

路径压缩

 public int root(int p) 
    int root = p;
    while (root != id[root])
        root = id[root];
    while (p != root) 
        int newp = id[p];
        id[p] = root;
        p = newp;
    
    return root;

详细的性能分析比较复杂,反正就是很快啦。

Applications

并查集有很多应用,上面的动态连接问题就算,视频里还有个物理系统方面的渗透(percolation)问题的例子。

percolation1

黑色表示方块是闭合的,如果上下存在连通的白色路径,则认为这个系统是可以渗透的。这是一个抽象的模型,实际上比如说可以是一块材料,白色表示可导电,那么系统渗透的话,整块材料就可以导电之类的。

其中方块是白色的概率为 p ,当 N 很大的时候,存在一个阈值 p* ,若 p >= p* 则几乎可以确定系统是可以渗透的。但这样的阈值函数图像很陡峭,问题就是如何求出这个阈值。

percolation2

Monte Carlo Simulation

这个问题,蒙特卡罗模拟可以解决。该方法随机地把黑色方块变成白色,直到系统可以渗透,然后用白色方块所占的比例来近似 p* 。举例来说, 下面近似的 p* = 204/400 = 5.1 。

percolation3

再重复做多次这样的模拟,就能得到比较精确的阈值 p* 。其实第一个编程作业就是渗透问题,里面是多次模拟后再求均值求方差,最后用概率论的知识算了 p* 置信度为 95% 的取值区间。。。

并查集是用在判断系统是否渗透上的。我们用 0 到 N^2 - 1 给每个方块编号,把相邻(上下左右)的白色方块合并起来,要是一个连通分量里同时含有第一行和最后一行的方块,那么系统就是可以渗透的。一个个检查第一行和最后一行的方块有点麻烦,我们可以假装上下各有一个方块,上面的和第一行全部方块都相连,下面类似。那么我们只要判断这两个虚拟方块是否连通即可,连通则系统可以渗透。

percolation4

这样在随机打开方块之后,要把它和相邻的白色方块(如果有的话)连接起来,再判断那两个虚拟方块是否连通,如果不连通则继续随机打开直到它们连通,连通则系统可以渗透,就可以用白方块个数除 N^2 得到近似的阈值啦。

最后,因为这其实就是第一次编程作业的题目,我具体的实现等到下次再说。

132.1.001union-find|并查集(代码片段)

@(132-ACM|算法)Algorithm|Coursera-byRobertSedgewick>Tip:FocusonWHATisreallyimportant!>Don'tjustcopyit!>Don'tlookatthesubtitle>Practiceisthekey.JustDoit!BackupCoursera-Algorithmsbooks 查看详情

算法设计:union-find算法实现(代码片段)

在上周的算法设计课程中,我们学习了UNION-FIND算法,该算法用来对不相交集进行查询与合并操作,但任何优秀的算法都必须要用实际的代码来进行实现,接下来我们就来看看具体的代码实现1. 不相关集数据结构的存储方式 ... 查看详情

并查集(union-find)(代码片段)

Date:2019-06-2313:42:531//定义2intfather[N];//father[1]=2,即2是1的父亲,根结点用father[i]=i表示34//初始化5for(inti=1;i<=N;i++)6father[i]=i;//初始时有N个独立的集合78//查找9//对于给定的结点,寻找其根结点10intFind(intx)1112if(father[x]==x)13r 查看详情

4.union-find算法(代码片段)

  算法的主题思想:  1.优秀的算法因为能够解决实际问题而变得更为重要;  2.高效算法的代码也可以很简单;  3.理解某个实现的性能特点是一个挑战;  4.在解决同一个问题的多种算法之间进行选择时,科学方法是... 查看详情

并查集(union-find)(代码片段)

一、基本操作:1、Find:当且仅当两个元素属于相同的集合时,返回相同的名字2、Union:将两个不相交的集合合并为一个不想交的集合。应用:在等价关系中,判断两个元素之间是否有关系或者添加等价关系。二、基本数据结构1... 查看详情

算法(第四版)之并查集(union-find算法)(代码片段)

...一点算法,但效果甚微并查集,在这本书的第一章1.5中叫做union-find算法,但在其他地方这个叫做并查集,就是说一系列点的连通问题,比如,我们有十个点,分别记作0~9:加入我们要把2和4连接起来怎么表示呢?首先我们会想到,给所有的点... 查看详情

并查集详解(代码片段)

目录并查集(Union-Find)初始化的两种方法:并集的三种方法:查集的两种方法:“并“、”查”的基本方法与优化源代码并查集(Union-Find)常用来解决动态连通性问题。曾有外国网友在StackExchange上发起过投票,选出世界十大有... 查看详情

小橙书阅读指南(十三)——连通性算法(union-find)(代码片段)

上一章我大概说明了什么是图论以及无向图的基础概念,本章我们要研究一种更普遍的算法——连通性算法。它属于图论的分支,也是一种抽象算法。在深入算法之前,我们先提出一个具体的问题:假设在空间中存在N个点,我... 查看详情

union-find并查集算法(代码片段)

一、动态连通性(DynamicConnectivity)Union-Find算法(中文称并查集算法)是解决动态连通性(DynamicConectivity)问题的一种算法。动态连通性是计算机图论中的一种数据结构,动态维护图结构中相连信息。简单的说就是,图中各个节点... 查看详情

如何检测社交网络中两个人是否是朋友关系(union-find算法)(代码片段)

前言春节放假会了老家,停更了很多天,这是年后连夜肝出来的第一篇文章,先来聊聊春节放假期间发生的事,这次回家遇到了我学生时代的女神,当年她在我心目中那是"出淤泥而不染、濯清涟而不妖"没想到这次回家遇到了她... 查看详情

Union-Find leetcode问题超过时限

】Union-Findleetcode问题超过时限【英文标题】:Union-Findleetcodequestionexceedingtimelimit【发布时间】:2018-09-0507:12:42【问题描述】:我正在leetcodehttps://leetcode.com/problems/sentence-similarity-ii/description/上解决这个问题,该问题涉及实现union-fin... 查看详情

union-find

1.算法复杂度与设计的数据结构有关设计成有一个数组id,id[]元素保存其相连接的点的编号值如下所示,采用这种类型的数据结构图1publicintFind(intp)2{3//找出分量的名车,寻找分量的根结点值4//寻找p节点所在组的根节点,根节点... 查看详情

并查集(union-find)

 目的:主要用来解决动态连通性问题(数据结构用来表征站点之间的连通性,算法主要利用数据结构,解决问题,比如,判断站点之间是否连通。由此,数据结构的特性对算法性能有着最直接的影响,数据结构和算法设计就... 查看详情

并查集(union-find)

常见问题:首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有... 查看详情

684.redundantconnection(代码片段)

此题可以使用两种思路来解决:DFSUnion-Find 以下是使用上一篇的数据结构Union-Find来处理的代码:/***LeetCode_146*https://leetcode.com/problems/redundant-connection/description/*https://www.youtube.com/watch?v=4hJ721ce010&list=LLaIZDn4w2rZnhRNMRMelhfg**/classSol... 查看详情

算法(第4版)-1.5案例研究:union-find算法

问题→动态连通性:当程序从输入中读取了整数对pq时,如果已知的所有整数对都不能说明p和q是相连的,那么则将这一对整数写入到输出中。如果已知的数据可以说明p和q是相连的,那么程序应该忽略pq这对整数并继续处理输入... 查看详情

pta编程题13filetransfer(代码片段)

其它pta数据结构编程题请参见:pta这道题考察的是union-find并查集。开始把数组中每个元素初始化为-1,代表没有父节点。为了使树更加平衡,可以让每一个连通分量的树根的负值代表这个连通分量包含的节点数,然后在union时把... 查看详情

二值图像连通域标记算法优化(代码片段)

...的方法不同,又细分为DFSTwo-Pass(使用DFS处理等价对)和Union-Find Two-Pass(使用并查集处理等价对)。如果硬要给这三种算法排序的话,大概是Union-Find Two-Pass> Seed-Filling > DFSTwo-Pass,反正我写的程序是这样的... 查看详情