現在,我是用乙個初學者的眼光來寫並查集,此文最初寫於我學並查集的那天,後經過多次修改。
1:並查集是什麼
並查集是乙個具有多個連通分支的圖,他擁有合併兩個連通分支,和查詢兩個元素是否位於同乙個連通分支的功能。
2:並查集的簡單應用
並查集解決什麼問題:假如有一些點,你知道哪些點是直接相連的,但實際上間接相連也是相連,要你求有幾個連通分支(即分為幾塊),以及任意倆個點是否位於同乙個連通分支。
3:並查集在現實中的解釋
我舉個例子。假如有乙個村,你知道其中一些人有親屬關係,求最多有幾個家族。比如你知道你和你爸爸有關係,又知到你爸爸和你姑姑有關係,又知道你姑姑和你媽媽有關係,那你們是不是就是乙個家族,雖然沒有直接說你和媽媽有關係,但通過相連可以確定。而並查集就是查你分為幾個家族,以及是否同時乙個家族。
4:並查集的實現及**
如何實現並查集?你是不是想直接用二維陣列儲存各元素之間的關係,那樣很麻煩的,用遍歷演算法的時間複雜度太大了。那怎麼辦,你們之間可以形成乙個樹形結構,只要根節點相同就可以了就證明在同乙個連通分支中。就是說比如你們家族的代言人為你媽媽,然後看你和乙個人的關係就看你們的代言人是不是同乙個。當然要分層。
設立乙個陣列int pre[50010],這個陣列記錄了他的父親節點是誰。如pre[100]=10代表了100的父節點是10,**當乙個數的父節點是它自己的時候,證明了這個數就是根節點。**當兩個元素的根節點相同時就是位於同乙個集合中。
下面這個就是查詢根節點的函式
int
find
(int x)
//尋找x的根節點
return r;
//只有當最終r==pre[r]時,即r為原來x的根節點時才會終止上面的迴圈,最後返回根節點
}
那實際上,怎麼合併一塊一塊呢?假如有乙個a與b相連,怎麼表示他們的關係呢?實際上,就是令其中乙個的根節點的父節點變為另外乙個的根節點就好了。
void
join
(int x,
int y)
//合併x和y,只要讓其中乙個的根節點變為另外乙個根節點的父節點即可
這樣合併會有什麼缺陷呢?在find操作中,實際的操作步數為x到其根節點的路徑長度+1(邊權為1),所以對於合併的樹來說,我們渴望節點的平均深度盡量小,這樣平均查詢的次數就比較少,我們下面講一種相對簡單的優化方法,更優的方法在我後續的文章中會講到。
初始化(開始的時候,因為沒給哪些點是相連的,所以所有點的父節點都是自己,即所有點都是根節點)
int pre[max]
;//集合index的類別,或者用parent表示
int rank[max]
;//集合index的層次,通常初始化為0
int data[max]
;//計畫index的資料結構型別
//初始化集合
void
make_pre
(int i)
查詢父節點
int
find
(int x)
return r;
}
接下來是合併,怎麼合併更好。我們前面不是設立了乙個rank嗎,他代表了以i為根節點的這棵樹的高度。而合併的方法就是讓高度較低的樹的根節點的父節點變為高度較高樹的根節點。
void
union
(int i,
int j)
}
想要例題?先學並查集的路徑壓縮。很簡單的,並且以後用到的並查集大多都是要路徑壓縮的。 並查集初學(1)
出處 github 並查集 union find sets 是一種非常精巧而實用的資料結構,它主要用於處理一些不相交集合的合併問題。一些常見的用途有求連通子圖 求最小生成樹的 kruskal 演算法和求最近公共祖先 least common ancestors,lca 等。使用並查集時,首先會存在一...
並查集初學
並查集是一種樹型的資料結構,用於處理一些不相交集合的合併及查詢問題。並查集的思想是用乙個陣列表示了整片森林 parent 樹的根節點唯一標識了乙個集合,我們只要找到了某個元素的的樹根,就能確定它在哪個集合裡。並查集用在一些有 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 ...