演算法系列之二十八 並查集(不相交集合)

2021-09-23 23:29:36 字數 3292 閱讀 9506

一 概述

並查集(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.初始化 包括對所有單個的資料建立乙個單獨的集合 即根據題目...