并查集(Union-Find Disjoint Sets)

并查集是一种树型的数据结构,主要适用于解决一些元素的分组问题。它管理一系列不相交的集合,并支持两种操作:

  • 合并(Union):把两个不相交的集合合并为一个集合。
  • 查询(Find):查询 两个元素是否在统一个集合中。

概述

在这里插入图片描述

并查集的重要思想在于,用集合中的一个元素代表集合。从上面可以看出,最终得到的是一个树状结构,要寻找集合的元素的代表,只需要一层一层的往上访问父节点,直到找到父节点为自身的根节点。

基本操作

如果共有 n n n个元素,我们可以使用一个数组来存储每个元素的父节点。其代码为:

int[] parent = new int[n];
for (int i = 0; i < len; i++) {
    parent[i] = i;
}

查找操作

对于并查集的查找操作,可以用递归的方法实现对代表元素的查询:一层一层的访问父节点,直至根节点。

int find(int v) {
    return v == parent[v] ? v : find(parent[v]);
}

对于两个元素我们可以通过查找操作查找根节点,来判断两个元素是否属于同一个集合。

合并操作

对于合并操作,可以先找到两个集合的代表元素,然后将前者的父节点设置为后者(当然也可以将后者的父节点设置为前者)。

void union(int i, int j) {
    int x = find(i), y = find(j);
    if (x == y) return;
    parent[x] = y;
}

优化

路径压缩合并

并查集的操作已经完成,但是并查集所形成的树可能是一条单链。对于这样的树越深,我们想要从底部找到根节点会变得越来越难。因此我们可以将一条单链转化为下面的形式:

在这里插入图片描述

上面的实现过程是:在查询的过程中,把沿途的每个节点的父节点都设为根节点即可。

int find(int v) {
    return v == parent[v] ? v : (parent[v] = find(parent[v]));
}

按秩合并

由于上面的路径压缩只在查找元素根节点时起作用,所以加入路径压缩算法之后并查集也不始终都是只有两层树。

在这里插入图片描述

上面两种合并方法,如果可以选择的话我们更希望是后者(即使有路径压缩,但路径压缩也是会消耗时间的),因为后者的合并方法不会使树的深度加深。这样的话可以使用一个数组来记录每个树的深度,合并时比较两个根节点的深度,把深度较小者往较大者上合并。

路径压缩和按秩合并如果一起使用,时间复杂度接近 O ( n ) O(n) O(n)

int[] rank = new int[n];
int[] parent = new int[n];
for (int i = 0; i < len; i++) {
    parent[i] = i;
    rank[i] = 1;
}

void union(int i, int j) {
    int x = find(i), y = find(j);
    if (x == y) return;
    if (rank[x] <= rank[y]) {
        parent[x] = y;
        rank[y] += rank[x];
    } else {
        parent[y] = x;
        rank[x] += rank[y];
    }
}

题目链接

  1. 128. 最长连续序列

参考文献:

  1. 算法学习笔记(1) : 并查集
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值