並查集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
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章