☘用Python來實現Disjoint Set (併查集/ Union-Find)

更新於 發佈於 閱讀時間約 1 分鐘

之前,已經學會了Linked List, 並且知道如何用Linked List來實作Queue 和 Stack。


今天,我們將用Python list來實現Disjoint Set (併查集,另外也有人稱之為Union-Find)。


Disjoint Set適合用於處理一些子集合的合併根節點的查找操作。
這種資料結構在圖論中非常有用,特別是在解決連通性相關問題的應用


定義

一個森林狀的資料結構。

一開始,每個ID對應到一顆樹,根節點都指向自己

整體看,就是有n顆樹(Tree)構成的森林(Forest)

raw-image

支援的操作與介面


DS.__init__(self, size)

初始化Disjoint Set,大小為參數size所指定


一開始,每個ID對應到一顆樹,根節點都指向自己

整體看,就是有n顆樹(Tree)構成的森林(Forest)

raw-image


DS.find(x)

尋找ID x所在的根節點(代表屬於哪個集合)


raw-image


DS.union(x, y)

合併ID x所在的集合 和 ID y所在的集合。

通常合併時,會以集合的size(也有課本稱之為rank)為依據。

size小的合併到size大的。

如果size相同,則合併到前者x所在的集合。


raw-image


raw-image



優點

1.使用path-compression之後,可以在 O (α(N)) ~ O(1) 常數時間內find()。

2.使用path-compression之後,可以在 O (α(N)) ~ O(1) 常數時間內union()。

缺點

1.相對不直覺,需要以陣列或字典去記錄每個ID的根結點。


Disjoint Set的class定義 與 建構子(初始化函式)

class DisjointSet:

def __init__(self, size):
self.parent = list(range(size))
self.rank = [1] * size

Disjoint Set常見的function實現


1.find(x)


尋找ID x所在的根節點(代表屬於哪個集合)。

並且沿路順便更新,把長輩節點的根結點都指向最上層的root。

這種技巧稱之為path compression。


時間複雜度: O (α(N)) ~ O(1) 常數時間

    def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # 路徑壓縮
return self.parent[x]

2.union(x, y)


合併ID x所在的集合 和 ID y所在的集合。

通常合併時,會以集合的size(也有課本稱之為rank)為依據。

size小的合併到size大的。

如果size相同,則合併到前者x所在的集合。


時間複雜度: O (α(N)) ~ O(1) 常數時間

    def union(self, x, q):
rootX = self.find(x)
rootY = self.find(q)

if rootX != rootY:
# 按size (rank)合併
if self.rank[rootX] > self.rank[rootY]:
self.parent[rootY] = rootX
elif self.rank[rootX] < self.rank[rootY]:
self.parent[rootX] = rootY
else:
self.parent[rootY] = rootX
self.rank[rootX] += 1

完整的Disjoint Set實作和程式碼,
底層是Python dictionary。


class DisjointSet:
def __init__(self, size):
self.parent = list(range(size))
self.rank = [1] * size

def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # 路徑壓縮
return self.parent[x]

def union(self, x, q):
rootX = self.find(x)
rootY = self.find(q)

if rootX != rootY:
# 按size (rank)合併
if self.rank[rootX] > self.rank[rootY]:
self.parent[rootY] = rootX
elif self.rank[rootX] < self.rank[rootY]:
self.parent[rootX] = rootY
else:
self.parent[rootY] = rootX
self.rank[rootX] += 1

def test():
# Example

# ID = 0, 1, 2, ..., 9
ds = DisjointSet(10)

print("After initialization")
for i in range(10):
print(f"root of {i} = {ds.find(i)}")

ds.union(1, 2)

print("\nAfter union(1, 2)")
for i in range(10):
print(f"root of {i} = {ds.find(i)}")

ds.union(3, 4)

print("\nAfter union(1, 2) and union(3, 4)")
for i in range(10):
print(f"root of {i} = {ds.find(i)}")

ds.union(1, 3)
print("\nAfter union(1, 2), union(3, 4), union(1, 3)")
for i in range(10):
print(f"root of {i} = {ds.find(i)}")

if __name__ == '__main__':
test()

測試輸出

After initialization
root of 0 = 0
root of 1 = 1
root of 2 = 2
root of 3 = 3
root of 4 = 4
root of 5 = 5
root of 6 = 6
root of 7 = 7
root of 8 = 8
root of 9 = 9

After union(1, 2)
root of 0 = 0
root of 1 = 1
root of 2 = 1
root of 3 = 3
root of 4 = 4
root of 5 = 5
root of 6 = 6
root of 7 = 7
root of 8 = 8
root of 9 = 9

After union(1, 2) and union(3, 4)
root of 0 = 0
root of 1 = 1
root of 2 = 1
root of 3 = 3
root of 4 = 3
root of 5 = 5
root of 6 = 6
root of 7 = 7
root of 8 = 8
root of 9 = 9

After union(1, 2), union(3, 4), union(1, 3)
root of 0 = 0
root of 1 = 1
root of 2 = 1
root of 3 = 1
root of 4 = 1
root of 5 = 5
root of 6 = 6
root of 7 = 7
root of 8 = 8
root of 9 = 9

結語


Disjoint Set其實就是對應到數學中的集合、集合合併的操作

讀著可以試著舉幾個小範例,用紙筆追蹤驗算法來驗證輸出結果,是否和預期相符合。



Disjoin Set相關的演算法練習題與詳解


🗿字典應用: Most Stones Removed with Same Row or Column_LC#947

留言
avatar-img
留言分享你的想法!
小松鼠-avatar-img
發文者
2024/08/30
林燃(創作小說家)
小松鼠-avatar-img
發文者
2024/08/29
動手學Python/資料結構/演算法 的目錄提及了這篇文章,趕快過去看看吧!
avatar-img
小松鼠的演算法樂園
95會員
427內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
2024/10/10
從Python 內建deque資料結構的角度切入, 同時了解deque 與 FIFO Queue相關的function用法。 collections.deque是一種兩端點皆可進出的雙端佇列 在兩端點高效地在O(1)常數時間內添加和刪除元素。 這使得deque非常適合實現FIFO Queue
Thumbnail
2024/10/10
從Python 內建deque資料結構的角度切入, 同時了解deque 與 FIFO Queue相關的function用法。 collections.deque是一種兩端點皆可進出的雙端佇列 在兩端點高效地在O(1)常數時間內添加和刪除元素。 這使得deque非常適合實現FIFO Queue
Thumbnail
2024/09/27
井字遊戲(OOXX)的遊戲描述 Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。 每回合兩個玩家輪流選一個位置,先讓自己的符號(是 X 或 O)在 水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
Thumbnail
2024/09/27
井字遊戲(OOXX)的遊戲描述 Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。 每回合兩個玩家輪流選一個位置,先讓自己的符號(是 X 或 O)在 水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
Thumbnail
2024/09/23
深入探討圖(Graph)的基本概念 及 最短路徑Shortest Path的尋找。 我們專注於廣度優先搜尋(BFS)演算法,以等權圖的最短路徑為例, 詳細說明如何利用BFS從起點擴散到終點,並且提供詳細的程式碼範例。 透過實作,讀者能夠更清楚理解圖論及BFS的應用,並體會水波紋擴散模型的重要性。
Thumbnail
2024/09/23
深入探討圖(Graph)的基本概念 及 最短路徑Shortest Path的尋找。 我們專注於廣度優先搜尋(BFS)演算法,以等權圖的最短路徑為例, 詳細說明如何利用BFS從起點擴散到終點,並且提供詳細的程式碼範例。 透過實作,讀者能夠更清楚理解圖論及BFS的應用,並體會水波紋擴散模型的重要性。
Thumbnail
看更多
你可能也想看
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
Array可以說是各種語言除了基本型別之外,最常用的資料型別與容器之一了。 Array 這種連續格子狀的資料結構,在Python要怎麼表達呢? 建立一個空的陣列 最簡單也最直接的寫法就是 array = [] # Python list [] 就對應到大家熟知的array 陣列型態的資料結
Thumbnail
Array可以說是各種語言除了基本型別之外,最常用的資料型別與容器之一了。 Array 這種連續格子狀的資料結構,在Python要怎麼表達呢? 建立一個空的陣列 最簡單也最直接的寫法就是 array = [] # Python list [] 就對應到大家熟知的array 陣列型態的資料結
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
Dictionary 字典 和 Set 集合 字典(Dictionary)是 Python 中一個常用的資料結構,用於儲存一組鍵值對(Key-Value pairs)。集合(Set)是 Python 中的一種無序、可變的資料結構,用於存儲多個元素,且集合中的元素是唯一的(不重複)
Thumbnail
Dictionary 字典 和 Set 集合 字典(Dictionary)是 Python 中一個常用的資料結構,用於儲存一組鍵值對(Key-Value pairs)。集合(Set)是 Python 中的一種無序、可變的資料結構,用於存儲多個元素,且集合中的元素是唯一的(不重複)
Thumbnail
你有沒有試著畫出家族樹或公司組織架構圖呢?如果有,那你其實已經初步了解「組合模式 (Composite Pattern)」了!這種模式就是用來處理這類包含部分和整體的樹狀結構。這篇文章,我們就來深入探討它是如何運作的。
Thumbnail
你有沒有試著畫出家族樹或公司組織架構圖呢?如果有,那你其實已經初步了解「組合模式 (Composite Pattern)」了!這種模式就是用來處理這類包含部分和整體的樹狀結構。這篇文章,我們就來深入探討它是如何運作的。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News