概念:在某些應用中,我們要檢查兩個元素是否屬於同乙個集合,或者將兩個不同的集合合併為乙個集合。這是不相交集合經常處理的兩種操作:查詢和合併,我們成為並查集。
基本思想:
1.標示乙個集合
選擇集合中某個固定的元素作為集合的代表,讓它作為整個集合唯一的標識。一般來說,選取的代表是任意的。也就是說,到底選擇集合中的哪個元素作為它的代表是無關緊要的。
2.樹的思想
在並查集中,我們對於集合的表示利用樹的思想,乙個集合可以看做一棵樹,樹根即代表該集合的標識。如果兩個集合在同乙個樹中,則它們是同乙個集合;合併兩個集合,即是對兩棵樹進行合併。
使用:若有一堆連續的元素0~n,則可以設定陣列a[n],每乙個元素的值作為陣列的下標使用。而陣列裡存放的值,則是這個下標元素所在集合的代表。在初始化時,沒有任何類似於「兩個元素同屬乙個集合」資訊,故我們在初始話時可以認為每個元素獨自構成乙個集合,集合的代表當然就是它自己,故初始化時**如下:
int a[/*乙個常量*/];
for(int i=0;i
a[i]=i;
然後我們定義兩個操作:查詢和合併。
1.查詢find:對於乙個數,若i==a[i],則說明i的代表是自己,也就是說i是乙個集合的代表。若i!=a[i],則說明i不是乙個集合的代表,我們繼續查詢a[i]的代表(i代表的代表,依然是i的代表)。舉個例子來說,張三的上司是李四,李四的上司是王五,王五是整個公司的boss。現在要查詢張三所在的公司(實際就是查詢這個公司的boss司王五),我們發現a[張三]==李四,a[張三]!=張三,所以我們繼續查詢張三的上司——李四,的上司。李四的上司a[李四]==王五,然後再查詢王五的上司,此時發現王五的上司就是自己,即a[王五]==王五,故我們知道王五是這個公司的頂頭上司,則王五就是張三所在公司這個集合的代表。具體實現如下:
int find(int x)
這個函式是沒問題的,只是有待優化,試想:對於上述例子,若張三的上司是李四,李四的上司是王五,王五的上司是趙六,趙六的上司是劉七,劉七的上司是王八(?)……則查詢張三的boss時,要經歷乙個「張三問李四,李四問王五,王五問趙六,趙六問劉七,劉七問王八,王八告訴劉七自己是boss,劉七告訴王五王八是boss,王五再往下傳……直到傳到張三」的過程,這樣一來一回,涉及的六個人都知道了自己的boss是王八。但若是按照這個函式來看,下次再有人問他們誰是他們的boss,他們還要一層一層問上去,豈不麻煩大矣?所以我們在一層一層遞迴的同時,更新所涉及的a[i],方便後面再有查詢的需要。具體實現如下:
int find(int x)
當然也有很玄學的寫法如下
int find(int x)
2.合併union
在使用並查集時,我們是用某乙個集合中的元素來代表整個集合的,那麼在合併兩個集合時,只需將其中任意乙個集合的代表元素作為另乙個集合代表元素的代表(例如上述公司要和洋企業合併,洋企業有個叫alice的女boss,然後裡面還有bob,john兩個員工,現在要合併兩個公司,只需要讓alice認王八做boss就可,bob和john的上司在他們下一次查詢自己的boss時會被路徑壓縮自動更新(當然讓王八認alice做boss效果是一樣的))。具體**如下:
void union(int x,int y)
3.判斷兩個元素是否同屬乙個集合query
這個就不必再說了吧……有了find()就有了這個,包括上面的合併操作,因為可以省去條件判斷,寫成一條單語句,所以很多人習慣直接合併而不是再寫union函式,這個也是。上**:
bool query(int x,int y)
例題:codevs 1073 家族
這道題之於並查集,就如八皇后之於遞迴,算是模板類題目了。
題目描述 description
若某個家族人員過於龐大,要判斷兩個是否是親戚,確實還很不容易,現在給出某個親戚關係圖,求任意給出的兩個人是否具有親戚關係。 規定:x和y是親戚,y和z是親戚,那麼x和z也是親戚。如果x,y是親戚,那麼x的親戚都是y的親戚,y的親戚也都是x的親戚。
輸入描述 input description
第一行:三個整數n,m,p,(n<=5000,m<=5000,p<=5000),分別表示有n個人,m個親戚關係,詢問p對親戚關係。 以下m行:每行兩個數mi,mj,1<=mi,mj<=n,表示ai和bi具有親戚關係。 接下來p行:每行兩個數pi,pj,詢問pi和pj是否具有親戚關係。
輸出描述 output description
p行,每行乙個』yes』或』no』。表示第i個詢問的答案為「具有」或「不具有」親戚關係。
樣例輸入 sample input
6 5 3
1 21 5
3 45 2
1 31 4
2 35 6
樣例輸出 sample output
yesyesno
資料範圍及提示 data size & hint
n<=5000,m<=5000,p<=5000
不多說,上**:
#include#includeusing namespace std;
const int maxn=5001;
int n,m,p,par[maxn];
int find(int x)
void un(int x,int y)
bool query(int x,int y)
int main()
for (i=1;i<=p;i++)
return 0;
}
並查集 學習
yx th000 cherish yimi 昨天和今天學習了並查集和 trie 樹,並練習了三道入門題目,理解更為深刻,覺得有必要總結一下,這其中的內容定義之類的是取自網路,操作的說明解釋及程式的注釋部分為個人理解。並查集學習 l 並查集 union find sets 一種簡單的用途廣泛的集合.並...
並查集 並查集
本文參考了 挑戰程式設計競賽 和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 ...
並查集的學習
並查集,是關於樹的結構的知識運用,可看成是多個樹的關係建立。即森林!每一棵樹代表乙個集合,每棵樹的根即為該集合的代表。總分為兩大操作,查詢與合併。用陣列來建立乙個並查集,陣列下標代表元素,下標對應的值代表父節點,全部初始化為 1,根節點為乙個集合的元素個數,陣列的長度為並查集的初始連通分量的個數。並...