演算法 並查集

2022-08-13 04:30:20 字數 2303 閱讀 9862

並查集是一種用途廣泛的資料結構,能夠快速地處理集合的合併和查詢問題,並且實現起來非常方便,在很多場合中都有著非常巧妙的應用,。

本文首先介紹並查集的定義、原理及具體實現,然後以其在最小生成樹演算法中的乙個經典應用為例講解其具體使用方法。

並查集是一種樹型的資料結構,用於處理一些不相交集合的合併及查詢問題。

並查集在使用中通常以森林來表示,每個集合組織為一棵樹,並且以樹根節點為代表元素。實際中以乙個陣列father[x]即可實現,表示節點x的父親節點。另外用乙個變數n表示節點的個數。但為了提公升效能,通常還用乙個陣列rnk[x]表示節點x的子樹的深度,具體後面解釋。

const int mx = 100000 + 1; //最大節點個數

int n; //節點個數

int father[mx]; //節點的父親

int rnk[mx]; //節點的子樹深度

並查集通常支援三種操作:初始化、查詢、合併。

初始化把每個點所在集合初始化為其自身,即n個節點就有n個集合。遍歷一次n個節點,把每個的父親初始為自己,子樹的深度初始化為0。

void makeset() //初始化

查詢元素所在的集合,即根節點。因為乙個集合只用根節點作為代表元,查詢的時候只要沿著father陣列一直往上走直到根節點為止,而根節點的父親為其本身,於是**為:

int findsetoriginal(int x) //非路徑壓縮查詢

但實際中會做乙個稱為路徑壓縮的優化。因為從該節點x到根節點可能會有很長的一條路徑,查詢的時間複雜度極端情況下為o(n)。我們可以在查詢的過程中,把每個節點的父親都指向跟節點,於是查詢完成之後原本長度為n的一條路徑變成了n條長度為1的路徑,這些節點的查詢時間複雜相應變成了o(1)。路徑壓縮的實現如下所示:

int findset(int x) //遞迴路徑壓縮查詢

但實際中遞迴演算法可能會造成棧溢位的問題,以下為相應的非遞迴演算法。主要思想是先找到該集合的根節點,然後把路徑上的節點的父親都改為根節點。

int findsetnonrecursive(int x) //非遞迴路徑壓縮查詢

return root; //返回根節點

}

將兩個元素所在的集合合併為乙個集合。合併的時候先使用2中的查詢函式找到兩個集合的根節點。如果根節點相同,說明屬於同乙個集合,則不需要合併。如果不同,只需把乙個根節點的父親指向另乙個根節點即可。

實際中使用了乙個稱為按秩合併的優化,因為直接合併可能產生一棵深度很深的樹,這不利於後續的查詢。前面的rnk[x]陣列表示節點x的秩,即該節點子樹的深度。合併時我們總是把秩小的節點的父親指向秩大的節點之上,這樣可以盡量較少新生成的樹的深度。當兩個節點的秩相同時,新生成樹的根節點的秩需要加1,因為子樹的深度增加了1,否則子樹深度沒有變化,秩也不需要改變。

int unionset(int x, int y) //合併

並查集(union-find sets)是一種非常精巧而實用的資料結構,它主要用於處理一些不相交集合的合併問題。一些常見的用途有求連通子圖、求最小生成樹的 kruskal 演算法和求最近公共祖先(least common ancestors, lca)等。

使用並查集時,首先會存在一組不相交的動態集合 s=

,一般都會使用乙個整數表示集合中的乙個元素。

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

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

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

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

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

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

注意:尋找祖先時我們一般採用遞迴查詢,但是當元素很多亦或是整棵樹變為一條鏈時,每次find_set(x)都是o(n)的複雜度,有沒有辦法減小這個複雜度呢?

答案是肯定的,這就是路徑壓縮,即當我們經過"遞推"找到祖先節點後,"回溯"的時候順便將它的子孫節點都直接指向祖先,這樣以後再次find_set(x)時複雜度就變成o(1)了,如下圖所示;可見,路徑壓縮方便了以後的查詢。

並查集演算法

所謂並查集,它是乙個集合,這個集合的元素也是集合,他支援三種操作 makeset x 建立乙個只有乙個元素x的集合x0,將這個集合放入並查集中 findset x 在並查集中尋找乙個元素s 注意並查集的元素s也是集合 滿足 x屬於s union x,y 將並查集中的元素s1,s2合併,其中x屬於s1...

並查集演算法

並查集是一種樹型的資料結構,用於處理一些不相交集合 disjoint sets 的合併及查詢問題。常常在使用中以森林來表示。讓每個元素構成乙個單元素的集合,也就是按一定順序將屬於同一組的元素所在的集合合併。1 makeset s 建立乙個新的並查集,包含s個單元素集合。2 union x,y 把x ...

並查集演算法

includeint pre 10 int find int x 查詢祖先節點 int i x,j while i r 壓縮路徑 return r void join int x,int y int main 2 食物鏈問題 description 動物王國中有三類動物a,b,c,這三類動物的食物鏈...