並查集基礎

2021-08-15 14:33:44 字數 2634 閱讀 5810

並查集的原理、實現與應用

什麼是並查集

如果給出各個元素之間的聯絡,要求將這些元素分成幾個集合,每個集合中的元素直接或間接有聯絡。在這類問題中主要涉及的是對集合的合併和查詢,因此將這種集合稱為並查集。

什麼是等價類

在並查集中,同乙個集合中的元素直接或者間接地有聯絡,我們就把這些元素稱為屬於同乙個等價類。等價類具有三個基本性質:

(用x~y表x與y等價)

自反性:如x~x則x~x;

對稱性:如x~y則y~x;

傳遞性:如x~y且y~z則x~z;

等價類應用

設初始有一集合s=

依次讀若干事先定義的等價對

0~4,3~1,6~10,8~9,7~4,6~8,3~5,2~11,11~0

我們想把每次讀入乙個等價對後,把等價集合合併起來。

則每讀入乙個等價對後的集合狀態是:(用{}表示乙個等價集合,初始時每個元素為乙個集合)

初始      ,,,,,,,,,,,

0 ~ 4      ,,,,,,,,,,

3 ~ 1      , ,,,,,,,,

6 ~ 10    ,,,,,,,, 

8 ~ 9      ,,,,,,,

7 ~ 4      ,,,,,,

6 ~ 8      ,,,,,

3 ~5      ,,,,

2 ~ 11    ,,,

11 ~ 0    ,,

並查集對等價關係的處理方式

並查集對等價關係的處理思想是開始把每乙個物件看作是乙個單元素集合,然後依次按順序(就是讀入等價對)將屬於同一等價類的元素所在的集合合併。在此過程中將重複地使用乙個搜尋運算,確定乙個元素在哪乙個集合中。

當讀入乙個等價對ab時,先檢測這兩個等價對是否同屬乙個集合,如是,則不用合併。不是,則用個合併演算法把這兩個包含ab的集合合併,使兩個集合的任兩個元素都是等價的(由傳遞性)。

並查集的實現方式

通常實現並查集有兩種資料結構:鍊錶和森林。森林是較好的並查集實現方式。因此,我們就以樹作為資料結構來實現並查集的操作。

並查集的表示方法

可以將森林中的一棵樹都看成是某些元素的集合,從而可以用樹來表示並查集。

而樹的儲存方法有很多種,如記錄陣列法、鍊錶等,表示方法包括父親表示法、兒子表示法、父親兒子表示法、等;每個結點儲存的資訊包括:結點資料、兒子結點位置、父結點位置等。

而用樹來表示並查集時,根據並查集的演算法特點,樹的儲存結構採用父親表示法,即用乙個一維陣列p表示每個元素的父結點的下標位置。

例如:p[3] = 4 就是說3號結點的父親是4號結點。如果乙個結點的父結點是它自己的話,我們就認為它是乙個集合的根結點。

根結點代表了乙個集合

並查集中每個元素都存在等價關係,那麼用哪個元素來代表整個集合呢?由於樹的根代表了一棵樹,那麼很自然地,根結點就代表了乙個並查集。

這樣,要判斷兩個元素是否屬於同乙個並查集時,演算法就非常簡單:只要比較兩個元素所在的樹的根結點是否相等,如果相等則屬於同乙個並查集;否則必須是不同的並查集。

另外,要合併兩個並查集也非常簡單:只要將一棵樹的根作為另一棵樹的兒子即可。

並查集的三種基本操作

並查集是一種可以方便地進行以下三種操作的資料結構:

初始化並查集:makeset

查詢元素x所在的集合,返回根:find(x)

合併兩個元素在所的集合:   union(x,y)

初始化並查集:makeset

初始時,每個元素就是乙個集合

void makeset(int n)
查詢元素x所在的集合:find(x)

根據樹的特點,樹的根是值和下標相等的元素,因此基本的演算法如下:

int findset(int x)
帶路徑壓縮的find(x)

在while迴圈中,把x的所有祖先結點的父結點都直接設為集合的根結點,這樣在查詢的過程中同時也改變了樹的形態,減小了樹的深度,這就叫路徑壓縮。雖然這增加了時間,但以後的find會快。平均效能而言,這是個高效方法。

實現:

int findset(int x)
合併兩個元素在所的集合:   union(x,y)

合併操作首先找出x,y所屬集合的根結點,如果根結點相等,則說明x,y本來就在同乙個集合,用不著合併,直接退出。

否則,進行啟發式合併。定義rank作為合併的啟發函式值,剛建立的新集合rank為0,當兩個rank相同的集合合併時,隨便選一棵樹作為新根,並把它的rank加1;否則rank大的樹作為新根。

在有路徑壓縮的前提下,啟發式合併的效率提高並不多。因此可以省略。

非啟發式合併:

void unionset(int x, int y)
並查集的時間複雜度

可以證明,經過啟發式合併和路徑壓縮之後的並查集,執行m次查詢的複雜度為o(mα(m))

其中α(m)是ackermann函式的某個反函式,你可以近似的認為它是小於5的。所以並查集的單次查詢操作的時間複雜度也幾乎是常數級的。

所以,並查集是一種非常高效的資料結構

並查集基礎

並查集問題描述 在一些有n個元素的集合應用問題中,我們通常是在開始時讓每個元素構成乙個單元素的集合,然後按一定順序將屬於同一組的元素所在的集合合併,其間要反覆查詢乙個元素在哪個集合中。這一類問題近幾年來反覆出現在資訊學的國際國內賽題中,其特點是看似並不複雜,但資料量極大,若用正常的資料結構來描述的話...

並查集基礎

並查集 disjoint set 是一種可以動態維護若干個不重疊的集合,並支援合併與查詢的資料結構。1.get,查詢乙個元素屬於哪個集合 2.merge 把兩個集合合併成乙個集合 並查集的儲存 使用乙個陣列fa儲存父節點 初始根的父節點設為自己,一開始每個節點自成乙個集合。int fa size f...

並查集 並查集

本文參考了 挑戰程式設計競賽 和jennica的github題解 陣列版 int parent max n int rank max n void init int n int find int x else void union int x,int y else 結構體版 struct node ...