並查集
一、定義
並查集是一種樹型的數據結構,用於處理一些不相交集合(Disjoint Sets)的合並及查詢問題。常常在使用中以森林來表示。
集合定義方法: “代表元法”,即 每個集合選擇一個固定的元素,作為整個集合的“代表”。
二、基本操作
Find —— 查詢一個元素屬於哪一個集合
Merge —— 把兩個集合合並成一個大集合
代碼示例:
三、路徑壓縮與按秩合並
——並查集的 奇技淫巧
①路徑壓縮
Get時, 將訪問過的節點直接指向樹根。 均攤復雜度
代碼示例:
int find(int x) { return x == fa[x] ? fa[x] : fa[x] = find(fa[x]); }
按秩合並
“秩”:樹的深度(未路徑壓縮) / 集合大小 。均攤復雜度
將”秩“記錄在 “代表元素” , 合並時, 將“秩”較小的樹根 作為 “秩”較大的樹根的子節點。
代碼示例:
void unionn(int x, int y){//按大小合並 int u=Get(x), v=Get(y); if(u != v) { if(size[u]<size[v]) f[u]=v, size[v]+=size[u];//按大小合並每次要更新大小 else f[v]=u, size[u]+=size[v]; } }
void unionn(int x, int y){//按深度合並 int u=Get(x), v=Get(y); if(u != v) { if(high[u] < hige[v]) f[u] = v; else { f[v]=u; if(high[u]==high[v]) high[u]++;//按深度合並只有在兩樹高度相等的時候更新 } } }
注:
①同時采用 “路徑壓縮” 和 “按秩合並” 優化的並查集, 每次Get操作復雜度可進一步降低到。
即為 反阿克曼函數
②如果題目需要維護明確的父子關系而用到了按秩合並的話,是不能用路徑壓縮的。一旦用了路徑壓縮,會破壞樹的形態,原來的節點會直接壓縮到祖先上,這樣一來我們調用的時候父子關系發生了改變,造成了算法的錯誤。
③並查集能在一張無向圖中維護節點之間的連通性(實際上可以維護許多具有傳遞性的關系)。
④並查集能夠快速跳過無用集合
普通並查集:
邊帶權並查集:
擴展域並查集: