並查集模版

一.並查集

博客地址:http://www.cnblogs.com/cyjb/p/UnionFindSets.html

並查集(Union-find Sets)是一種非常精巧而實用的數據結構,它主要用於處理一些不相交集合的合併問題。一些常見的用途有求連通子圖、求最小生成樹的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。

使用並查集時,首先會存在一組不相交的動態集合 S={S1,S2,⋯,Sk}S={S1,S2,⋯,Sk},一般都會使用一個整數表示集合中的一個元素。

每個集合可能包含一個或多個元素,並選出集合中的某個元素作爲代表。每個集合中具體包含了哪些元素是不關心的,具體選擇哪個元素作爲代表一般也是不關心的。我們關心的是,對於給定的元素,可以很快的找到這個元素所在的集合(的代表),以及合併兩個元素所在的集合,而且這些操作的時間複雜度都是常數級的。

並查集的基本操作有三個:

makeSet(s):建立一個新的並查集,其中包含 s 個單元素集合。

unionSet(x, y):把元素 x 和元素 y 所在的集合合併,要求 x 和 y 所在的集合不相交,如果相交則不合並。

find(x):找到元素 x 所在的集合的代表,該操作也可以用於判斷兩個元素是否位於同一個集合,只要將它們各自的代表比較一下就可以了。

並查集的實現原理也比較簡單,就是使用樹來表示集合,樹的每個節點就表示集合中的一個元素,樹根對應的元素就是該集合的代表,如圖 1 所示。


圖中有兩棵樹,分別對應兩個集合,其中第一個集合爲 {a,b,c,d}{a,b,c,d},代表元素是 aa;第二個集合爲 {e,f,g}{e,f,g},代表元素是 ee

樹的節點表示集合中的元素,指針表示指向父節點的指針,根節點的指針指向自己,表示其沒有父節點。沿着每個節點的父節點不斷向上查找,最終就可以找到該樹的根節點,即該集合的代表元素。

現在,應該可以很容易的寫出 makeSet 和 find 的代碼了,假設使用一個足夠長的數組來存儲樹節點(很類似之前講到的靜態鏈表),那麼 makeSet 要做的就是構造出如圖的森林,其中每個元素都是一個單元素集合,即父節點是其自身:


相應的代碼如下所示,時間複雜度是 O(n)O(n)


 

接下來,就是 find操作了,如果每次都沿着父節點向上查找,那時間複雜度就是樹的高度,完全不可能達到常數級。這裏需要應用一種非常簡單而有效的策略——路徑壓縮。

路徑壓縮,就是在每次查找時,令查找路徑上的每個節點都直接指向根節點,如圖所示。


我準備了兩個版本的 find操作實現,分別是遞歸版和非遞歸版,不過兩個版本目前並沒有發現有什麼明顯的效率差距,所以具體使用哪個完全憑個人喜好了。


 

最後是合併操作 unionSet,並查集的合併也非常簡單,就是將一個集合的樹根指向另一個集合的樹根,如圖所示。


這裏也可以應用一個簡單的啓發式策略——按秩合併。該方法使用秩來表示樹高度的上界,在合併時,總是將具有較小秩的樹根指向具有較大秩的樹根。簡單的說,就是總是將比較矮的樹作爲子樹,添加到較高的樹中。爲了保存秩,需要額外使用一個與uset 同長度的數組,並將所有元素都初始化爲0

 

 

完整代碼:

 

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