1、概念:
在一些有n個元素的集合應用問題中,我們通常是在開始時讓每個元素構成乙個單元素的集合,然後按一定順序將屬於同一組的元素所在的集合合併,其間要反覆查詢乙個元素在哪個集合中。這一類問題近幾年來反覆出現在資訊學的國際國內賽題中,其特點是看似並不複雜,但資料量極大,若用正常的資料結構來描述的話,往往在空間上過大,計算機無法承受;即使在空間上勉強通過,執行的時間複雜度也極高,根本就不可能在比賽規定的執行時間(1~3秒)內計算出試題需要的結果,只能用並查集來描述。
2、定義:
並查集是一種樹型的資料結構,用於處理一些不相交集合(disjoint sets)的合併及查詢問題。常常在使用中以森林來表示。
集就是讓每個元素構成乙個單元素的集合,也就是按一定順序將屬於同一組的元素所在的集合合併。
3、操作:
初始化把每個點所在集合初始化為其自身。
通常來說,這個步驟在每次使用該資料結構時只需要執行一次,無論何種實現方式,時間複雜度均為o(n)。
查詢查詢元素所在的集合,即根節點。
合併將兩個元素所在的集合合併為乙個集合。
通常來說,合併之前,應先判斷兩個元素是否屬於同一集合,這可用上面的「查詢」操作實現。
4、例題
若某個家族人員過於龐大,要判斷兩個是否是親戚,確實還很不容易,給出某個親戚關係圖,求任意給出的兩個人是否具有親戚關係。 規定:x和y是親戚,y和z是親戚,那麼x和z也是親戚。如果x,y是親戚,那麼x的親戚都是y的親戚,y的親戚也都是x的親戚。
第一行:三個整數n,m,p,(n< =5000,m< =5000,p< =5000),分別表示有n個人,m個親戚關係,詢問p對親戚關係。 以下m行:每行兩個數mi,mj,1< =mi,mj< =n,表示mi和mj具有親戚關係。 接下來p行:每行兩個數pi,pj,詢問pi和pj是否具有親戚關係。
p行,每行乙個』yes』或』no』。表示第i個詢問的答案為「具有」或「不具有」親戚關係。
初步分析覺得本題是乙個圖論中判斷兩個點是否在同乙個連通子圖中的問題。對於題目中的樣例,以人為點,關係為邊,建立無向圖如下:
圖0-0-1
比如判斷3和4是否為親戚時,我們檢查3和4是否在同乙個連通子圖中,結果是在,於是他們是親戚。又如7和10不在同乙個連通子圖中,所以他們不是親戚。
用圖的資料結構的最大問題是,我們無法存下多至(m=)2 000 000條邊的圖,後面關於演算法時效等諸多問題就免談了。
用圖表示關係過於「奢侈」了。其實本題只是乙個對分離集合(並查集)操作的問題。
我們可以給每個人建立乙個集合,集合的元素值有他自己,表示最開始時他不知道任何人是它的親戚。以後每次給出乙個親戚關係a, b,則a和他的親戚與b和他的親戚就互為親戚了,將a所在集合與b所在集合合併。對於樣例資料的操作全過程如下:
輸入關係 分離集合
初始狀態
(2,4)
(5,7)
(1,3)
(8,9)
(1,2)
(5,6)
(2,3)
最後我們得到3個集合, , ,於是判斷兩個人是否親戚的問題就變成判斷兩個數是否在同乙個集合中的問題。如此一來,需要的資料結構就沒有圖結構那樣龐大了。
演算法需要以下幾個子過程:
(1) 開始時,為每個人建立乙個集合sub-make-set(x);
(2) 得到乙個關係後a,b,合併相應集合sub-union(a,b);
(3) 此外我們還需要判斷兩個人是否在同乙個集合中,這就涉及到如何標識集合的問題。我們可以在每個集合中選乙個代表標識集合,因此我們需要乙個子過程給出每個集合的代表元sub-find-set(a)。於是判斷兩個人是否在同乙個集合中,即兩個人是否為親戚,等價於判斷sub-find-set(a)=sub-find-set(b)。
有了以上子過程的支援,我們就有如下演算法。
problem-relations(n, m, a1,…,am, b1,…,bm, q, c1,…,cq, d1,…,dq)
1 for i←1 to n
2 do sub-make-set(i)
3 for i←1 to m
4 do if sub-find-set(ai) != sub-find-set(bi)
5 then sub-union(ai, bi)
6 for i←1 to q
7 do if sub-find-set(ci)=sub-find-set(di)
8 then output 「yes?」
9 else output 「no?」
解決問題的關鍵便為選擇合適的資料結構實現並查集的操作,使演算法的實現效率最高。
本題的輸入資料量很大,這使得我們的程式會在輸入中花去不少時間。如果你用pascal寫程式,可以用庫函式settextbuf為輸入檔案設定緩衝區,這可以使輸入過程加快不少。如果你是用c語言的話,就不必為此操心了,系統會自動分配緩衝區。
5、c++**描述
1 #include2 #include3 #include4 #include5view codeusing
namespace
std;67
int father[50002
],a,b,m,n,p;
8int find(int
x)19
intmain()
2032
for(i=1;i<=p;i++)
3342
return0;
43 }
並查集簡單描述
並查集演算法不支援分割乙個集合。用集合中的某個元素來代表這個集合,該元素稱為集合的代表元。乙個集合內的所有元素組織成以代表元為根的樹形結構。對於每乙個元素 parent x 指向x在樹形結構上的父親節點。如果x是根節點,則令parent x x。對於查詢操作,假設需要確定x所在的的集合,也就是確定集...
並查集演算法
所謂並查集,它是乙個集合,這個集合的元素也是集合,他支援三種操作 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 ...