並查集由乙個整數型的陣列和兩個函式構成。陣列pre[ ]記錄了每個點的前導點是什麼,函式find是查詢,join是合併。
int pre[
1005];
intfind
(int x)
//查詢x的前乙個節點
//最終r為根節點
//路徑壓縮 :把所有子節點的上乙個節點都改為根節點
int i=x, j;
while
(i!=r)
return r;
//返回每個點的直接根節點
}void
join
(int x,
int y)
//合併兩個附屬於不同根節點的子節點
求互相沒有聯絡的組的個數
bool t[
1005]=
;for
(int i=
1; i<=n; i++
)//因為join的合併實在find函式後面,最後直接合併兩個根節點,
t[find
(i)]=1
;//每次呼叫find函式,n個點後,所有的點都直屬於它的根節點。
int num=0;
for(
int i=
1; i<=n; i++)if
(t[i]
) num++
;//num的個數為互相沒有聯絡的組數
並查集的優化
並查集有兩種優化方式,第一種是路徑壓縮,第二種是按秩合併。
路徑壓縮又分為非遞迴實現的和遞迴實現的
非遞迴實現的就是上面的**,它不適用於求解帶權並查集的問題,而遞迴實現的路徑壓縮雖然空間消耗相比較大,但是如果求解帶權問題就要用它了。
下面是遞迴實現的路徑壓縮
int
find
(int x)
按秩合併
一般情況下合併兩個節點時,誰做誰的父親都無所謂,這時就出現了按秩優化。假設有兩個根節點,第乙個根節點的深度是100個,第二個根節點的深度是10個,如果讓第乙個根節點合併到第二個下面,那麼在下一次find的過程中,最壞的情況下第乙個根節點下面的最後乙個節點,就要find100次才能到根節點,這樣就浪費了時間。如果讓深度小的根節點合併到深度大的根節點下面,就會縮短很大的時間。
void
join
(int x,
int y)
else
if(rank[fy]
>rank[fx]
)else
}}
帶權並查集
普通的並查集就是描述兩個點之間的關係,比如說a的父親節點是b,c的父親節點是a,b是a和c的根節點。而如果現在告訴了你a和b兩個節點之間的關係,a和c之間的關係,也就是所謂的加上了邊權,那麼解題的時候就必須加上邊權資訊,這就成了帶權並查集。
帶權並查集只是在普通的並查集find函式和join函式中加了一些操作。
比如說初始每個結點的根節點的距離deep[i]都是0(每個點都是自己的老大),每個結點所在的集合有sum[i]=1個(初始只有自己乙個集合),通過合併一些點求它到根節點的距離。
那麼在find的時候就要更新deep值
int
find
(int x)
只是這樣的操作deep每次都是0,根本不會更新,所以還需要乙個sum陣列,這樣當兩個節點合併的時候,某乙個集合的數量就會增加,而被合併的那個節點到跟節點的距離就是合併後作為根節點的那個結合的數量,最後再把這個集合的數量加上被合併的節點所在的集合的數量。
雖然只是改變了根節點的資訊,其他的子節點並沒有改變,不要著急,在下次用find查詢某乙個節點的資訊的時候,deep[i]就會在find函式裡被自動改變了。
void
join
(int x,
int y)
}
並查集,帶權並查集
題意 ignatius過生日,客人來到,他想知道他需要準備多少張桌子。然而一張桌子上面只能坐上相互熟悉的人,其中熟悉可定義成為a與b認識,b與c認識,我們就說a,b,c相互熟悉 例如a與b熟悉and b與c熟悉,d與e熟悉,此時至少需要兩張桌子。輸入 t表示樣例個數,n表示朋友個數,朋友從1到n編號...
並查集和帶權並查集
並查集是乙個很高效演算法,理解起來也很簡單,寫起來更簡單。fat i i 找到乙個點的祖先 int findfat int x 二中的方法肯定不好,因為如果資料比較極端,那麼並查集就退化成乙個鏈了 如果加入了路徑壓縮,並查集這個演算法就更高效了。int findfat int x 遞迴寫法 int ...
並查集與帶權並查集
1.找點的祖先 fa i i 並查集的快主要在於路徑壓縮。1 遞迴寫法 int find int x 2 非遞迴寫法 int find int x return r 2.合併 合併2者的集合。void merge int x,int y 3.帶權並查集 一般是存下一些2者之間的具體的數量關係或者是統...