并查集专题

关于并查集的题目不少,官方给的数据是 30 道(截止 2020-02-20),但是有一些题目虽然官方没有贴并查集标签,但是使用并查集来说确非常简单。这类题目如果掌握模板,那么刷这种题会非常快,并且犯错的概率会大大降低,这就是模板的好处。

我这里总结了几道并查集的题目:

  • 547. 朋友圈

  • 721. 账户合并

  • 990. 等式方程的可满足性

看完这里的内容,建议拿上面的题目练下手,检测一下学习成果。

概述

并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。有一个联合-查找算法(Union-find Algorithm)定义了两个用于此数据结构的操作:

  • Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。

  • Union:将两个子集合并成同一个集合。

由于支持这两种操作,一个不相交集也常被称为联合-查找数据结构(Union-find Data Structure)或合并-查找集合(Merge-find Set)。为了更加精确的定义这些方法,需要定义如何表示集合。一种常用的策略是为每个集合选定一个固定的元素,称为代表,以表示整个集合。接着,Find(x) 返回 x 所属集合的代表,而 Union 使用两个集合的代表作为参数。

形象解释

比如有两个司令。司令下有若干军长,军长下有若干师长。。。

我们如何判断某两个师长是否属于同一个司令呢(连通性)?

很简单,我们顺着师长,往上找,找到司令。如果两个师长找到的是同一个司令,那么就属于同一个司令。我们用 parent[x] = y 表示 x 的 parent 是 y,通过不断沿着搜索 parent 搜索找到 root,然后比较 root 是否相同即可得出结论。

以上过程涉及了两个基本操作findconnnected。并查集除了这两个基本操作,还有一个是union。即将两个集合合并为同一个。

如图有两个司令:

我们将其合并为一个联通域,最简单的方式就是直接将其中一个司令指向另外一个即可:

以上就是三个核心 API findconnnectedunion, 的形象化解释,下面我们来看下代码实现。

核心 API

find

def find(self, x):
    while x != self.parent[x]:
        x = self.parent[x]
    return x

connected

def connected(self, p, q):
    return self.find(p) == self.find(q)

union

def union(self, p, q):
    if self.connected(p, q): return
    self.parent[self.find(p)] = self.find(q)

完整代码模板

class UF:
    parent = {}
    def __init__(self, equations):
        # 做一些初始化操作

    def find(self, x):
        while x != self.parent[x]:
            x = self.parent[x]
        return x
    def union(self, p, q):
        if self.connected(p, q): return
        self.parent[self.find(p)] = self.find(q)
    def connected(self, p, q):
        return self.find(p) == self.find(q)

带路径压缩的代码模板

class UF:
    parent = {}
    def __init__(self, equations):
        # 做一些初始化操作
    def find(self, x):
        if x != self.parent[x]:
            parent[x] = find(parent[x])
        return parent[x]
    def union(self, p, q):
        if self.connected(p, q): return
        self.parent[self.find(p)] = self.find(q)
    def connected(self, p, q):
        return self.find(p) == self.find(q)

上面是递归的方式进行路径压缩,写起来比较简单。但是有栈溢出的风险。接下来我们看下迭代的写法:

class UF:
    parent = {}
    def __init__(self, equations):
        # 做一些初始化操作

    def find(self, x):
        # 根节点
        r = x
        while r != parent[r]:
            r = parent[r]
        k = x
        while k != r:
            # 暂存parent[k]的父节点
            j = parent[k]
            parent[k] = r
            k = j
        return r
    def union(self, p, q):
        if self.connected(p, q): return
        self.parent[self.find(p)] = self.find(q)
    def connected(self, p, q):
        return self.find(p) == self.find(q)

1、每日一荐 2020-03 汇总

2、『不要再问我头像如何变灰了,试试这几种滤镜吧!』

3、LeetCode专题 - 小岛题

4、一文带你AC四道题【位运算】

5、或许是一本可以彻底改变你刷 LeetCode 效率的题解书

6、外部排序:如何用 2GB内存给 20 亿个整数排序?

如果觉得文章不错,帮忙点个在看呗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集是一种常用的数据结构,用于管理一个不相交集合的数据。在并查集中,每个元素都有一个父节点指向它所属的集合的代表元素。通过查找和合并操作,可以判断两个元素是否属于同一个集合,并将它们合并到同一个集合中。 在解决某些问题时,可以使用并查集进行优化。例如,在区间查询中,可以通过优化并查集的查询过程,快速找到第一个符合条件的点。 对于拆边(destroy)操作,一般的并查集无法直接实现这个功能。但是可以通过一个巧妙的方法来解决。首先,记录下所有不会被拆除的边,然后按照逆序处理这些指令。遇到拆边操作时,将该边重新加入并查集中即可。 在实现并查集时,虽然它是一种树形结构,但只需要使用数组就可以实现。可以通过将每个元素的父节点记录在数组中,来表示元素之间的关系。通过路径压缩和按秩合并等优化策略,可以提高并查集的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [「kuangbin带你飞」专题并查集专题题解](https://blog.csdn.net/weixin_51216553/article/details/121643742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [并查集(详细解释+完整C语言代码)](https://blog.csdn.net/weixin_54186646/article/details/124477838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值