并查集Union Find

并查集:一种用于支持集合快速合并和查找操作的数据结构

  • O(1) 合并两个集合- Union
  • O(1) 查询元素所属集合- Find

Union Find 是一棵多叉树:

1、并查集的实现

1.1 底层数据结构

父亲表示法,用一个数组/哈希表记录每个节点的父亲是谁。

  • father[“Tom”] = “Nlear”
  • father[“Jim”] = “Soox”

1.2 查询所在集合

  • 用所在集合最顶层的老大哥节点来代表这个集合

1.3 合并两个集合

  • 找到两个集合中最顶层的两个老大哥节点A 和B
  • father[A] = B // or father[B] = A 如果无所谓谁合并谁的话

2、代码模板

2.1 初始化

使用哈希表或者数组来存储每个节点的父亲节点

def init(nums):
    n = len(nums)
    father = {}
    for i in range(n):
        father[i] = i

2.2 查找和压缩

  • 沿着父亲节点一路往上走就能找到根节点
  • 在找到根节点以后,还需要把一路上经过的点都指向根节点

def find(target, father):
    path = []
    while target != father[target]:
        path.append(target)
        target = father[target]
    for p in path:
        father[p] = target
    return target

2.3 合并

  • 找到两个元素所在集合的两个根节点a 和b
  • 将其中一个根节点的父指针指向另外一个根节点
def union(point_a, point_b, father):
    root_a = find(point_a)
    root_b = find(point_b)
    if root_a != root_b:
        father[root_a] = root_b

3、常见问题:

  1. 判断两个元素是否在同一个集合
  2. 获得某个集合的元素个数
  3. 统计当前集合个数

总结:

  • 跟连通性有关的问题,都可以使用BFS 和Union Find
  • 需要拆开两个集合的时候无法使用Union Find

3.1、连接图

给一个图中的n个节点, 记为 1 到 n . 在开始的时候图中没有边。
你需要完成下面两个方法:

  1. connect(a, b), 添加连接节点 ab 的边.
  2. query(a, b), 检验两个节点是否联通

来自:九章算法

链接:https://www.jiuzhang.com/solution/connecting-graph/#tag-highlight-lang-python

解析:简单并查集实现并和查操作。

class ConnectingGraph:

    def __init__(self, n):
        self.father = {}
        for i in range(1, n + 1):
            self.father[i] = i

    def connect(self, a, b):
        self.father[self.find(a)] = self.find(b)

    def query(self, a, b):
        return self.find(a) == self.find(b)
        
    def find(self, node):
        path = []
        while self.father[node] != node:
            path.append(node)
            node = self.father[node]
            
        for n in path:
            self.father[n] = node
            
        return node

3.2、连接图 II  

给一个图中的 n 个节点, 记为 1 到 n .在开始的时候图中没有边.
你需要完成下面两个方法:

  1. connect(a, b), 添加一条连接节点 a, b的边
  2. query(a), 返回图中含 a 的联通区域内节点个数

来自:九章算法

链接:https://www.jiuzhang.com/solution/connecting-graph-ii/#tag-highlight-lang-python

解析:在根节点上记录这个集合的元素个数并查集。同时维护一下个数即可。

class ConnectingGraph2:

    def __init__(self, n):
        self.father = {}
        self.count = {}
        for i in range(1, n + 1):
            self.father[i] = i
            self.count[i] = 1

    def connect(self, a, b):
        root_a = self.find(a)
        root_b = self.find(b)
        if root_a != root_b:
            self.father[root_a] = root_b
            self.count[root_b] += self.count[root_a]

    def query(self, a):
        return self.count[self.find(a)]

    def find(self, node):
        path = []
        while node != self.father[node]:
            path.append(node)
            node = self.father[node]
            
        for n in path:
            self.father[n] = node
            
        return node

3.3、连接图 III 

给一个图中的 n 个节点, 记为 1 到 n . 在开始的时候图中没有边.
你需要完成下面两个方法:

  1. connect(a, b), 添加一条连接节点 a, b的边
  2. query(), 返回图中联通区域个数

来自:九章算法

链接:https://www.jiuzhang.com/solution/connecting-graph-iii/#tag-highlight-lang-python

 

分析:实时维护区域的个数,即若在某一次合并中两个区域合并成一个,那么数量-1。

class ConnectingGraph3:
   
    def __init__(self, n):
        self.size = n
        self.father = {}
        for i in range(1, n + 1):
            self.father[i] = i

    def connect(self, a, b):
        root_a = self.find(a)
        root_b = self.find(b)
        if root_a != root_b:
            self.father[root_a] = root_b
            self.size -= 1
            
    def query(self):
        return self.size

    def find(self, node):
        path = []
        while node != self.father[node]:
            path.append(node)
            node = self.father[node]
        
        for n in path:
            self.father[n] = node
        return node

3.4、 账户合并

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/accounts-merge
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值