一 概述
並查集(disjoint set或者union-find set)是一種樹型的資料結構,常用於處理一些不相交集合(disjoint sets)的合併及查詢問題。
有乙個聯合-查詢演算法(union-find algorithm)定義了兩個操作用於此資料結構:
find:確定元素屬於哪乙個子集。它可以被用來確定兩個元素是否屬於同一子集。
union:將兩個子集合並成同乙個集合。
因為它支援這兩種操作,乙個不相交集也常被稱為聯合-查詢資料結構(union-find data structure)或合併-查詢集合(merge-find set)。其他的重要方法,makeset,用於建立單元素集合。有了這些方法,許多經典的劃分問題可以被解決。
為了更加精確的定義這些方法,需要定義如何表示集合。一種常用的策略是為每個集合選定乙個固定的元素,稱為代表,以表示整個集合。接著。find(x)返回x所屬集合的代表,而union(x,y)使用兩個集合的代表x,y作為引數。
二 主要操作
1.makeset(x)
2.find(x)
3.union(x,y)
2.1 makeset(x) 建立乙個新的集合建立乙個新的集合,其唯一成員(因為是其代表)就是x。因為集合是不相交的,故要求x沒有在其它集合**現過。
2.2 find(x) 包含x集合的代表
返回乙個指標,指向包含x的(唯一)集合的代表。
2.3 union(x,y) 合併兩個不相交集合
將包含x和y的動態集合合併成為乙個新的集合。所得集合的代表可以是兩個集合的任何成員。但在很多情況下,我們一般選擇兩個集合之前代表中的乙個作為新的代表。
三 不相交集合森林(有根樹表示集合)
不相交集合可以用鍊錶實現,但是還有一種更快的方法—–有根樹表示集合,樹中的每個節點都包含集合的乙個成員,每棵樹都表示乙個集合。如下圖:
左邊的樹表示集合{b,c,e,h}其c是代表;右邊的樹表示集合{d,f,g}其f是代表。
3.1 makeset(x)
makeset建立一棵僅包含乙個節點的樹。初始時父節點為自己。
#define n 100
//申請記憶體的大小
int parent[n];
// parent[x]表示x的父節點
void makeset(int x)
3.2 find(x)find(x)指向包含x的(唯一)集合的代表。沿著父節點指標一直找下去,直到找到樹根為止。
int find(int x)//if
// 沿著父節點指標尋找
find(parent[x]);
}
3.3 union(x,y)union操作使的一棵樹的根指向另一棵樹的根。如下圖:
// 合併
void union(int x,int y)
四 優化4.1 按秩合併其思想是使包含較少結點的樹指向包含較多結點的樹的根。我們並不顯示的記錄以每個結點為根的子樹的大小,而是採用一種能夠簡化分析的方法。對每個結點,我們用秩表示結點高度(從該結點到某一後代葉節點的最長路徑上邊的數目)的乙個上界。在按秩合併中,具有較小秩的根在union操作中指向較大秩的根。
rank[x]表示x節點的秩。當由makeset建立了乙個集合時,對應的樹中唯一節點的初始秩為0,每個find操作都不改變任何秩。
// parent[x]表示x的父節點 rank[x] 表示x的秩
void makeset(int x)
當對兩棵樹應用union時,有兩種情況:
(1) 當兩個秩不相等時,我們使具有較高秩的根稱為具有較小秩的根的父節點,但秩本身保持不變。
(2)當兩個秩相等時,任選乙個根作為父節點,並增加其秩的值。
void union(int x, int y)//if
if(rank[x] > rank[y])//if
else
if(rank[x] < rank[y])//else
else//else
}
4.2 路徑壓縮尋找祖先時,我們一般採用遞迴查詢,但是當元素很多亦或是整棵樹變為一條鏈時,每次find(x)都是o(n)的複雜度。為了避免這種情況,我們需對路徑進行壓縮,即當我們經過」遞推」找到祖先節點後,」回溯」的時候順便將它的子孫節點都直接指向祖先,這樣以後再次find(x)時複雜度就變成o(1)了,如下圖所示。可見,路徑壓縮方便了以後的查詢。
其中三角表示子樹,其根為所示節點。
// 帶路徑壓縮的find
int find(int x)//if
return
parent[x];
}
find是一種兩趟方法:一趟是沿查詢路徑上公升,直到找到根;另一趟是沿查詢路徑下降,一便更新每個節點,使之指向根節點。
五 複雜度分析
空間複雜度為o(n),建立乙個集合的時間複雜度為o(1),n次合併m查詢的時間複雜度為o(m alpha(n)),這裡alpha是ackerman函式的某個反函式,在很大的範圍內(人類目前觀測到的宇宙範圍估算有10的80次方個原子,這小於前面所說的範圍)這個函式的值可以看成是不大於4的,所以並查集的操作可以看作是與m成線性關係。
六 應用
並查集常作為另一種複雜的資料結構或者演算法的儲存結構。常見的應用有:求無向圖的連通分量個數,最近公共祖先(lca),帶限制的作業排序,實現kruskar演算法求最小生成樹等。
七 引用
並查集資料結構之並查集
演算法導論
並查集(不相交集合)
早上早早起來看kruscal的mst演算法,原來要用到不相交集合來實現。拿起 演算法導論 看完不相交集合這章,頓然茅塞頓開,終於完成並查集的基礎知識的學習。演算法導論 真是牛 不相交集合有兩種不同的實現,鍊錶表示和帶路徑壓縮的按秩合併策略。看到大家都比較喜歡用帶路徑壓縮的按秩合併策略,那麼我只認真研...
並查集(不相交集)ADT
等價關係 需要同時滿足下列三個性質的關係r 等價集合 如果乙個元素a 屬於集合s,則元素a的等價集合是集合s的乙個子集,它包含所有與元素a有等價關係的元素。輸入資料最初是n個元素 元素也是乙個集合 的集合,其中每個集合只含有乙個元素,且互不相同,也不存在等價關係,使得這些集合互不相交,此時只能進行兩...
並查集 不相交集 原理及JAVA實現
並查集 disjoint set或者union find set 是一種樹型的資料結構,常用於處理一些不相交集合 disjoint sets 的合併及查詢問題。在使用中常常以森林來表示。並查集主要有三個操作,分別為 初始化 查詢以及合併。1.初始化 包括對所有單個的資料建立乙個單獨的集合 即根據題目...