[算法系列之二十八]并查集(不相交集合)

[算法系列之二十八]并查集(不相交集合)

大家好,又见面了,我是全栈君。

一 概述

并查集(Disjoint set或者Union-find set)是一种树型的数据结构,经常使用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。

有一个联合-查找算法(union-find algorithm)定义了两个操作用于此数据结构:

Find:确定元素属于哪一个子集。它能够被用来确定两个元素是否属于同一子集。
Union:将两个子集合并成同一个集合。

由于它支持这两种操作,一个不相交集也常被称为联合-查找数据结构(union-find data structure)或合并-查找集合(merge-find set)。

其他的重要方法。MakeSet。用于建立单元素集合。

有了这些方法,很多经典的划分问题能够被解决。

为了更加精确的定义这些方法,须要定义怎样表示集合。

一种经常使用的策略是为每一个集合选定一个固定的元素,称为代表。以表示整个集合。

接着。Find(x)返回x所属集合的代表,而Union(x,y)使用两个集合的代表x,y作为參数。

二 主要操作

1.MakeSet(x)
2.Find(x)
3.Union(x,y)

2.1 MakeSet(x) 建立一个新的集合

建立一个新的集合,其唯一成员(由于是其代表)就是x。

由于集合是不相交的。故要求x没有在其他集合中出现过。

2.2 Find(x) 包括x集合的代表

返回一个指针,指向包括x的(唯一)集合的代表。

2.3 Union(x,y) 合并两个不相交集合

将包括x和y的动态集合合并成为一个新的集合。所得集合的代表能够是两个集合的不论什么成员。但在非常多情况下,我们一般选择两个集合之前代表中的一个作为新的代表。

三 不相交集合森林(有根树表示集合)

不相交集合能够用链表实现。可是还有一种更快的方法—–有根树表示集合。树中的每一个节点都包括集合的一个成员,每棵树都表示一个集合。

例如以下图:

这里写图片描写叙述

左边的树表示集合{b,c,e,h}其c是代表。右边的树表示集合{d,f,g}其f是代表。

3.1 MakeSet(x)

MakeSet创建一棵仅包括一个节点的树。初始时父节点为自己。

#define N 100

//申请内存的大小
int parent[N];

// parent[x]表示x的父节点
void MakeSet(int x){
    parent[x] = x;
}

3.2 Find(x)

Find(x)指向包括x的(唯一)集合的代表。沿着父节点指针一直找下去,直到找到树根为止。

int Find(int x){
    // 根节点即集合代表
    if(x == parent[x]){
        return x;
    }//if
    // 沿着父节点指针寻找
    Find(parent[x]);
}

3.3 Union(x,y)

Union操作使的一棵树的根指向还有一棵树的根。例如以下图:

这里写图片描写叙述

// 合并
void Union(int x,int y){
    x = Find(x);
    y = Find(y);
    parent[y] = x;
}

四 优化

4.1 按秩合并

其思想是使包括较少结点的树指向包括较多结点的树的根。

我们并不显示的记录以每一个结点为根的子树的大小,而是採用一种能够简化分析的方法。对每一个结点,我们用秩表示结点高度(从该结点到某一后代叶节点的最长路径上边的数目)的一个上界。在按秩合并中,具有较小秩的根在Union操作中指向较大秩的根。

rank[x]表示x节点的秩。当由MakeSet创建了一个集合时,相应的树中唯一节点的初始秩为0,每一个Find操作都不改变不论什么秩。

// parent[x]表示x的父节点 rank[x] 表示x的秩
void MakeSet(int x){
    parent[x] = x;
    rank[x] = 0;
}

当对两棵树应用Union时,有两种情况:
(1) 当两个秩不相等时。我们使具有较高秩的根称为具有较小秩的根的父节点。但秩本身保持不变。
(2)当两个秩相等时。任选一个根作为父节点,并添加其秩的值。

void Union(int x, int y){
    x = Find(x);
    y = Find(y);
    if(x == y) {
        return;
    }//if
    if(rank[x] > rank[y]){
        parent[y] = x;
    }//if
    else if(rank[x] < rank[y]){
        parent[x] = y;
    }//else
    else{
        rank[x]++;
    }//else
}

4.2 路径压缩

寻找祖先时,我们一般採用递归查找,可是当元素非常多亦或是整棵树变为一条链时。每次Find(x)都是O(n)的复杂度。为了避免这样的情况,我们需对路径进行压缩。即当我们经过”递推”找到祖先节点后,”回溯”的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find(x)时复杂度就变成O(1)了。例如以下图所看到的。可见,路径压缩方便了以后的查找。

这里写图片描写叙述

当中三角表示子树。其根为所看到的节点。

// 带路径压缩的Find
int Find(int x){
    // 根节点即集合代表
    if(x != parent[x]){
        // 更新节点x使之指向根
        parent[x] = Find(parent[x]);
    }//if
    return parent[x];
}

Find是一种两趟方法:一趟是沿查找路径上升,直到找到根;还有一趟是沿查找路径下降。一便更新每一个节点。使之指向根节点。

五 复杂度分析

空间复杂度为O(N)。建立一个集合的时间复杂度为O(1)。N次合并M查找的时间复杂度为O(M Alpha(N)),这里Alpha是Ackerman函数的某个反函数,在非常大的范围内(人类眼下观測到的宇宙范围估算有10的80次方个原子,这小于前面所说的范围)这个函数的值能够看成是不大于4的,所以并查集的操作能够看作是与m成线性关系。

六 应用

并查集常作为还有一种复杂的数据结构或者算法的存储结构。常见的应用有:求无向图的连通分量个数,近期公共祖先(LCA),带限制的作业排序,实现Kruskar算法求最小生成树等。

七 引用

并查集
数据结构之并查集
算法导论

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/116538.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 百度分享异步加载问题、分页,无效果解决[通俗易懂]

    百度分享异步加载问题、分页,无效果解决[通俗易懂]使用百度分享的时候,如果所涉及到的html部分是后加载进来的,如ajax等异步请求成功后,加载进来,那么百度分享就有可能出现错误。我在使用的时候,遇到了两个问题。在这里记录一下。1、无法把所需要分享的内容传值到百度分享里。  百度分享的配置里有两个值,bdText,bdDesc,这两个内容,分别分享标题和内容。  内容是异步加载进来的,所以在百度分享相关代码是在加载成功后运

    2022年10月8日
    0
  • 最短路径算法——Dijkstra算法——python3实现

    最短路径算法——Dijkstra算法——python3实现本文参考来自数据结构与算法分析java语言描述。问题描述问题分析实现过程如何使用数据变化表问题描述现有一个有向赋权图。如下图所示:问题:根据每条边的权值,求出从起点s到其他每个顶点的最短路径和最短路径的长度。说明:不考虑权值为负的情况,否则会出现负值圈问题。s:起点v:算法当前分析处理的顶点w:与v邻接的顶点dvdvd_v:从s到v的距离…

    2022年5月4日
    69
  • pycharm2021.2激活码【中文破解版】

    (pycharm2021.2激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlS32PGH0SQB-eyJsaWN…

    2022年3月26日
    71
  • 推荐一个 curl 命令转化为PHP代码的网站&&在线发起执行 curl 请求的网站

    推荐一个 curl 命令转化为PHP代码的网站&&在线发起执行 curl 请求的网站

    2022年2月18日
    38
  • origin画图标注_origin图线上的特殊符号怎么弄

    origin画图标注_origin图线上的特殊符号怎么弄Origin画图标签常见语法下标:-(x)上标:+(x)斜体:\i(x)加粗:\b(x)x下标y上标:=(x,y)

    2022年9月21日
    0
  • 多图详解缓冲区溢出问题

    多图详解缓冲区溢出问题蠕虫病毒是一种常见的利用Unix系统中的缺点来进行攻击的病毒。缓冲区溢出一个常见的后果是:黑客利用函数调用过程中程序的返回地址,将存放这块地址的指针精准指向计算机中存放攻击代码的位置,造成程序异常中止。为了防止发生严重的后果,计算机会采用栈随机化,利用金丝雀值检查破坏栈,限制代码可执行区域等方法来尽量避免被攻击。虽然,现代计算机已经可以“智能”查错了,但是我们还是要养成良好的编程习惯,尽量避免写出有漏洞的代码,以节省宝贵的时间!

    2022年7月12日
    38

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号