Disjoint set(並查集) data structure

JUAN425發表於2014-08-08

不相交集合(disjoint-set datastructure)資料結構又被稱為並查集(union-find data structure)資料結構。 

涉及到將n 個不同的元素分成一組不相交的子集合(disjoint subset)。  所謂的不相交(disjoint), 又被稱為nonoverlapping subset。 並查集(亦稱為不相交集合) support 2個重要的operations:

 (1)Find (x)操作: 尋找給定的元素屬於哪一個(唯一的)子集合。 這個Find 操作的返回的含有x的子集合S中 一個能夠代表這個子集合S的item(一般是S的最小的成員)。 我們可以通過比較2個Find 操作, 即find(x) 和 find(y) 來比較x 和 y 是否在同一個子集合中。

(2)Union 操作: 將兩個subset 合併成一個更大的subset。


除了上述的兩個操作, 還有一個操作叫做makeSet(x), 該操作的作用是建立一個新的集合, 它的唯一的成員(因而為代表)是x。因為各個集合是不相交的, 所以x 不會出現在別的集合中。


具有上述三個重要的關於並查集合的操作, 那麼眾多的partition problem 都能夠得到解決。




建立一個disjoint-set data structure 的最簡單的方法就是對於每一個set, 我們用linked list 實現它。 每一個list  的head 的element  作為代表代表著這個list(當然也就是代表著這個subset)。 

 makeSet 操作就是建立一個具有one element的 list 。 Union 操作就是將2個list 合併稱為一個list。 這是一個時間複雜度為O(1)的操作(constant-time operation)。

然而使用連結串列去實現每一個子集合的缺點就是導致Find 操作的時間複雜度為O(n)。 


不相交集合森林(disjoint-set forests)

所謂的Disjoint-set forest, 就是將每一個set 用樹結構(tree data structure)(不一定是二叉樹, 也可能是多叉樹) 實現(represented。 樹的每一個節點都holds a reference to its parrent node。 這是由 Bernard A. Galler and Michael J. Fischer 在1964年發明的。


在disjoint-set forest中, 每一個set 的representative 就是由這個set而建立起來的樹的根節點, Find 操作 是沿著parent nodes, 直至到達the root。 Union 是將一棵樹的根節點和另一棵樹的根節點合併起來。 組成一棵更大的樹。

相關的操作的實現過程如下:



上面使用樹, 樹的每個成員均指向它的父節點, 且每棵樹根是這棵樹的代表, 並且是其自己的父節點。 但是使用樹的這種表示的 直接演算法並不比使用連結串列 的直接演算法快, 因為我們建的樹的結構 是一棵高度不平衡(highly unbalanced)的樹,  但是通過引入兩種啟發式的策略(“按秩合併”(Union by rank) 和 “路徑壓縮”(path compression)), 我們可以得到一種漸進最優的 不相交集合 資料結構。


(1)策略一: 按秩合併(Union by rank)

這個方法是將一棵較小樹(即節點數少)的根結點 連線到 具有較大的樹的根節點上(attach the smaller tree to the root of the larger tree)。  因為影響各個操作(演算法)的是樹的深度, 所以將較小的樹新增到更深的樹的根上 並不會增加秩除非這兩個數的深度相同(秩會增加1)。

之所以使用“秩”這個術語而不是用“深度”, 是因為如果同時使用了路徑壓縮的策略, 秩 將不會與高度相同。 氮元素的秩 定義為0。 兩棵秩同為r 的樹合併為一個大樹的時候, 他們的秩為r + 1. 只使用按秩合併這個策略, 將會使得makeSet 和 Union 以及 Find 操作的最壞情況下的時間複雜度為O(logn)。 優化後的makeSet 和 Union 的虛擬碼如下:



第二個優化演算法被稱為“路徑壓縮”。 這個優化策略是, 每當我們對樹結構執行Find操作的時候, 我們就使用路徑壓縮策略對flattening the structure of the tree 。 關鍵是在路徑上的每個節點都可以直接連線到根上。 為了達到扁平化樹結構的效果, Find 向上遞迴的traverse樹, 改變每一個節點的引用(parent Reference)到根節點()root。 最終我們會得帶一個much flatter 的樹。 對以後直接或者間接的引用這些節點加速。  改進後的Find操作如下:

 



關於Union-Find data structure 的實現



實現演算法程式碼如下:






例如, 具體的, 如下:














相關文章