一 並查集 Union Find Set)

2021-06-21 04:51:38 字數 3046 閱讀 8713

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

鍊錶被普通用來計算並查集.表中的每個元素設兩個指標:乙個指向同一集合中的下乙個元素;另乙個指向表首元素。 

鏈結構的並查集 

採用鏈式儲存結構,在進行集合查詢時的演算法複雜度僅為o(1);但合併集合時的演算法複雜度卻達到了o(n)。如果我們希望兩種基本操作的時間效率都比較高的話,鏈式儲存方式就「力不從心」了。 

樹結構的並查集 

採用樹結構支援並查集的計算能夠滿足我們的要求。並查集與一般的樹結構不同,每個頂點紀錄的不是它的子結點,而是將它的父結點記錄下來。下面是樹結構的並查集的兩種運算方式 

⑴直接在樹中查詢 

⑵邊查詢邊「路徑壓縮」 

對應與前面的鏈式儲存結構,樹狀結構的優勢非常明顯:程式設計複雜度低;時間效率高。 

直接在樹中查詢 

集合的合併演算法很簡單,只要將兩棵樹的根結點相連即可,這步操作只要o(1)時間複雜度。演算法的時間效率取決於集合查詢的快慢。而集合的查詢效率與樹的深度呈線性關係。因此直接查詢所需要的時間複雜度平均為o(logn)。但在最壞情況下,樹退化成為一條鏈,使得每一次查詢的演算法複雜度為o(n)。 

邊查詢邊「路徑壓縮 

其實,我們還能將集合查詢的演算法複雜度進一步降低:採用「路徑壓縮」演算法。它的想法很簡單:在集合的查詢過程中順便將樹的深度降低。採用路徑壓縮後,每一次查詢所用的時間複雜度為增長極為緩慢的ackerman函式的反函式——α(x)。對於可以想象到的n,α(n)都是在5之內的。

並查集:(union-find sets)是一種簡單的用途廣泛的集合. 並查集是若干個不相交集合,能夠實現較快的合併和判斷元素所在集合的操作,應用很多。一般採取樹形結構來儲存並查集,並利用乙個rank陣列來儲存集合的深度下界,在查詢操作時進行路徑壓縮使後續的查詢操作加速。這樣優化實現的並查集,空間複雜度為o(n),建立乙個集合的時間複雜度為o(1),n次合併m查詢的時間複雜度為o(m alpha(n)),這裡alpha是ackerman函式的某個反函式,在很大的範圍內(人類目前觀測到的宇宙範圍估算有10的80次方個原子,這小於前面所說的範圍)這個函式的值可以看成是不大於4的,所以並查集的操作可以看作是線性的。它支援以下三中種操作:

-union (root1, root2) //並操作;把子集合root2併入集合root1中.要求:root1和 root2互不相交,否則不執行操作.

-find (x) //搜尋操作;搜尋單元素x所在的集合,並返回該集合的名字.

-ufsets (s) //建構函式。將並查集中s個元素初始化為s個只有乙個單元素的子集合.

-對於並查集來說,每個集合用一棵樹表示。

-集合中每個元素的元素名分別存放在樹的結點中,此外,樹的每乙個結點還有乙個指向其雙親結點的指標。

-設 s1= ,s2= ,s3=

-為簡化討論,忽略實際的集合名,僅用表示集合的樹的根來標識集合。

-為此,採用樹的雙親表示作為集合儲存表示。集合元素的編號從0到 n-1。其中 n 是最大元素個數。在雙親表示中,第 i 個陣列元素代表包含集合元素 i 的樹結點。根結點的雙親為-1,表示集合中的元素個數。為了區別雙親指標資訊( ≥ 0 ),集合元素個數資訊用負數表示。   

下標parent

集合s1, s2和s3的雙親表示:

s1 ∪ s2的可能的表示方法

const int defaultsize = 10;

class ufsets

ufsets & operator = ( ufsets const & value );//集合賦值

void union ( int root1, int root2 );

int find ( int x );

void unionbyheight ( int root1, int root2 ); };

ufsets::ufsets ( int s )

unsigned int ufsets::find ( int x )

void ufsets::union ( int root1, int root2 )

find和union操作效能不好。假設最初 n 個元素構成 n 棵樹組成的森林,parent[i] = -1。做處理union(0, 1), union(1, 2), …, union(n-2, n-1)後,將產生如圖所示的退化的樹。

執行一次union操作所需時間是o(1),n-1次union操作所需時間是o(n)。若再執行find(0), find(1), …, find(n-1), 若被

搜尋的元素為i,完成find(i)操作需要時間為o(i),完成 n 次搜尋需要的總時間將達到

union操作的加權規則

為避免產生退化的樹,改進方法是先判斷兩集合中元素的個數,如果以 i 為根的樹中的結點個數少於以 j 為根的樹中的結點個數,即parent[i] > parent[j],則讓 j 成為 i 的雙親,否則,讓i成為j的雙親。此即union的加權規則。

使用加權規則得到的樹

演算法總結 一 並查集

目的 解決元素分組問題 用途 1 判斷有向圖中是否產生環 2 維護無向圖的連通性,判斷兩個點是否在同乙個連通塊中 操作 1 初始化 每個集合的parent都是自己 2 查詢 查詢集合的parent 3 合併 把不相連的元素合併到同乙個集合中 1 初始化 假如有編號為1,2,3,n的n個元素,我們用乙...

並查集 並查集

本文參考了 挑戰程式設計競賽 和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 ...

並查集入門(普通並查集 帶刪除並查集 關係並查集)

什麼是並查集?通俗易懂的並查集詳解 普通並查集 基礎並查集 例題 題解 how many tables problem description lh boy無聊的時候很喜歡數螞蟻,而且,還給每乙隻小螞蟻編號,通過他長期的觀察和記錄,發現編號為i的螞蟻會和編號為j的螞蟻在一起。現在問題來了,他現在只有...