並查集:一種用於支持集合快速合併和查找操作的數據結構
- 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、常見問題:
- 判斷兩個元素是否在同一個集合
- 獲得某個集合的元素個數
- 統計當前集合個數
總結:
- 跟連通性有關的問題,都可以使用BFS 和Union Find
- 需要拆開兩個集合的時候無法使用Union Find
3.1、連接圖
給一個圖中的n
個節點, 記爲 1
到 n
. 在開始的時候圖中沒有邊。
你需要完成下面兩個方法:
connect(a, b)
, 添加連接節點a
,b
的邊.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
.在開始的時候圖中沒有邊.
你需要完成下面兩個方法:
connect(a, b)
, 添加一條連接節點 a, b的邊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
. 在開始的時候圖中沒有邊.
你需要完成下面兩個方法:
connect(a, b)
, 添加一條連接節點 a, b的邊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