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

更新於 2024/08/30閱讀時間約 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
90會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
在之前的教學中,已經學會了用雙向鏈結串列來實作Stack 堆疊。 今天,要用另一種底層資列結構,python list,來實作Stack 堆疊。 讀者可以從中發現,因為python list的功能和function實作已經很豐富, 所以使用起來,相當直覺,也簡單許多。
在這次的BMI(身體質量指標)計算的續集裡,將學會funciton的基本觀念與實作, 把常用的功能包裝成可重複利用的元件: function。
在之前的教學中,已經學會了Node和Linked List的實作, 用Python實現了單向鏈結串列Singly linked list、雙向鏈結串列Doubly linked list。 今天要承接之前打下的基礎,用雙向鏈結串列來實作Queue(佇列 或稱 隊列)。
在之前的教學中,已經學會了Node和Linked List的實作, 用Python實現了單向鏈結串列Singly linked list、雙向鏈結串列Doubly linked list。 今天要承接之前打下的基礎,用雙向鏈結串列來實作Stack 堆疊。
在資料結構與演算法裡, 最簡單的線性資料結構除了array之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list
在這次的BMI(身體質量指標)計算教學裡, 將學會如何用python來接收使用者輸入的訊息,並且做一些簡單的四則運算。
在之前的教學中,已經學會了用雙向鏈結串列來實作Stack 堆疊。 今天,要用另一種底層資列結構,python list,來實作Stack 堆疊。 讀者可以從中發現,因為python list的功能和function實作已經很豐富, 所以使用起來,相當直覺,也簡單許多。
在這次的BMI(身體質量指標)計算的續集裡,將學會funciton的基本觀念與實作, 把常用的功能包裝成可重複利用的元件: function。
在之前的教學中,已經學會了Node和Linked List的實作, 用Python實現了單向鏈結串列Singly linked list、雙向鏈結串列Doubly linked list。 今天要承接之前打下的基礎,用雙向鏈結串列來實作Queue(佇列 或稱 隊列)。
在之前的教學中,已經學會了Node和Linked List的實作, 用Python實現了單向鏈結串列Singly linked list、雙向鏈結串列Doubly linked list。 今天要承接之前打下的基礎,用雙向鏈結串列來實作Stack 堆疊。
在資料結構與演算法裡, 最簡單的線性資料結構除了array之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list
在這次的BMI(身體質量指標)計算教學裡, 將學會如何用python來接收使用者輸入的訊息,並且做一些簡單的四則運算。
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
就如同標題一樣,input的作用就是從使用者那裡獲取輸入,直到使用者輸入一段文本並按下 ENTER 鍵。 然而用戶輸入的數據(文本)都將作為字串被返回,並存儲在變數中。 接著我們舉個例,比如說我們在一段數據中需要獲取使用者的名稱,範例如下: name = input("請輸入你的名字:") #
Thumbnail
本篇內容介紹如何使用 moviepy library 將影片檔轉為mp3音樂檔。
Thumbnail
本文介紹了Python如何使用websockets進行雙向溝通,包括文字、json、xml和音訊的傳遞。特別著重於json資料交換格式,以及websockets通道的基本流程和關鍵的編碼與解碼。最終談到WebSocket對於傳統同步程式的轉變及對asyncio等套件的重要性。
Thumbnail
如何匯入Excel或CSV檔案? 如何更改欄位名稱? 如何從舊欄位中組合新欄位? 如何擷取舊欄位內容成新欄位? 如何篩選資料?
Thumbnail
「functools.partial」是Python中的一個標準函式庫,它可以讓我們基於既有的函式封裝成多種不同用途的函式,就如同上圖所示,我們設計了一個乘法(multiply)的函數,使用了partial讓函數的參數「c」固定下來依據用途不同變化出「double」、「triple」,這樣一來我
Thumbnail
您是否苦於網路資訊爆炸嗎? 教學何其多,但卻無法好好選擇的困境呢? 歡迎加入「🔒 阿Han的軟體心法實戰營」, 這裡不給您冗餘的雜訊, 單刀直入直接送您重點, 避開選擇障礙的困境, 讓您獲得業界標準的開發起手式, 成為Top 1的頂尖人才。 身為專業的軟體開發者的我們, 除了讓程式會動之外, 也
Thumbnail
根據泰勒定理,f(x)可以寫成右邊一長串的導數的組合 為了更好理解這個東西我們可以用python實作 首先定義f(x)和定義factorial怎麼算 然後寫泰勒定理 f(x) = f(a) + f'(a)(x-a) ....後面還有一串 注意公式往後面看其實是有規律的 例如從
Thumbnail
如果,你是個專業的工程師,在外面臨時遇到需要修改或測試專案的情況,但手邊又沒有電腦跟網路,又或是,今天你臨時需要外出一陣子,但又放不下自己的專案,你會怎麼辦呢?
Thumbnail
Connect database 因爲我們後端是用 django,所以我們要用 python 來操作 MongoDB,MongoDB 官方推薦的 python driver 是 pymongo,首先來安裝 在想使用的檔案內加入 myclient = pymongo.MongoClient("mong
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
就如同標題一樣,input的作用就是從使用者那裡獲取輸入,直到使用者輸入一段文本並按下 ENTER 鍵。 然而用戶輸入的數據(文本)都將作為字串被返回,並存儲在變數中。 接著我們舉個例,比如說我們在一段數據中需要獲取使用者的名稱,範例如下: name = input("請輸入你的名字:") #
Thumbnail
本篇內容介紹如何使用 moviepy library 將影片檔轉為mp3音樂檔。
Thumbnail
本文介紹了Python如何使用websockets進行雙向溝通,包括文字、json、xml和音訊的傳遞。特別著重於json資料交換格式,以及websockets通道的基本流程和關鍵的編碼與解碼。最終談到WebSocket對於傳統同步程式的轉變及對asyncio等套件的重要性。
Thumbnail
如何匯入Excel或CSV檔案? 如何更改欄位名稱? 如何從舊欄位中組合新欄位? 如何擷取舊欄位內容成新欄位? 如何篩選資料?
Thumbnail
「functools.partial」是Python中的一個標準函式庫,它可以讓我們基於既有的函式封裝成多種不同用途的函式,就如同上圖所示,我們設計了一個乘法(multiply)的函數,使用了partial讓函數的參數「c」固定下來依據用途不同變化出「double」、「triple」,這樣一來我
Thumbnail
您是否苦於網路資訊爆炸嗎? 教學何其多,但卻無法好好選擇的困境呢? 歡迎加入「🔒 阿Han的軟體心法實戰營」, 這裡不給您冗餘的雜訊, 單刀直入直接送您重點, 避開選擇障礙的困境, 讓您獲得業界標準的開發起手式, 成為Top 1的頂尖人才。 身為專業的軟體開發者的我們, 除了讓程式會動之外, 也
Thumbnail
根據泰勒定理,f(x)可以寫成右邊一長串的導數的組合 為了更好理解這個東西我們可以用python實作 首先定義f(x)和定義factorial怎麼算 然後寫泰勒定理 f(x) = f(a) + f'(a)(x-a) ....後面還有一串 注意公式往後面看其實是有規律的 例如從
Thumbnail
如果,你是個專業的工程師,在外面臨時遇到需要修改或測試專案的情況,但手邊又沒有電腦跟網路,又或是,今天你臨時需要外出一陣子,但又放不下自己的專案,你會怎麼辦呢?
Thumbnail
Connect database 因爲我們後端是用 django,所以我們要用 python 來操作 MongoDB,MongoDB 官方推薦的 python driver 是 pymongo,首先來安裝 在想使用的檔案內加入 myclient = pymongo.MongoClient("mong