1、 概述
並查集(disjoint set或者union-find set)是一種樹型的資料結構,常用於處理一些不相交集合(disjoint sets)的合併及查詢問題。
a.乙個集合是一棵樹,
b.根節點下標代表集合名稱,根節點的父為為負數,絕對值表示集合中的元素個數。
並查集的重要知識點:a.加權規則,即並兩個沒有交集的集合,元素個數少的集合加到元素個數多的集合裡
b.並集合後,為了改進樹的效能,使用摺疊規則來「壓縮路徑」。雖然增加了完成所有單個元素搜尋的時間,幾乎增加一倍。但是,他能減少最壞情況下一系列 搜尋操作所需時間
2、 基本操作
並查集是一種非常簡單的資料結構,它主要涉及3個基本操作,分別為:
a. 合併兩個不相交集合union(root1,root2)
b. find(x):搜尋單元素x所在的集合,並返回該集合的名字
c. ufsets(s):建構函式,將並查集中s個元素初始化為s個只有乙個單元素的子集合
(1) 合併兩個不相交集合(union(x,y))
合併操作很簡單:先設定乙個陣列father[x],表示x的「父親」的編號。那麼,合併兩個不相交集合的方法就是,找到其中乙個集合最父親的父親(也就是最久遠的祖先),將另外乙個集合的最久遠的祖先的父親指向它。
上圖為兩個不相交集合,b圖為合併後father(b):=father(g)
(2) 判斷兩個元素是否屬於同一集合(find_set(x))
本操作可轉換為尋找兩個元素的最久遠祖先是否相同。可以採用遞迴實現。
3、 優化
(1) find_set(x)時,路徑壓縮
尋找祖先時,我們一般採用遞迴查詢,但是當元素很多亦或是整棵樹變為一條鏈時,每次find_set(x)都是o(n)的複雜度。為了避免這種情況,我們需對路徑進行壓縮,即當我們經過」遞推」找到祖先節點後,」回溯」的時候順便將它的子孫節點都直接指向祖先,這樣以後再次find_set(x)時複雜度就變成o(1)了,如下圖所示。可見,路徑壓縮方便了以後的查詢。
(2) union(x,y)時,按秩合併
即合併的時候將元素少的集合合併到元素多的集合中,這樣合併之後樹的高度會相對較小。
4、 程式設計實現
int father[max]; /* father[x]表示x的父節點*/
int rank[max]; /*rank[x]表示x的秩*/
void make_set(int x)
/* 查詢x元素所在的集合,回溯時壓縮路徑*/
int find_set(int x)
return father[x];
} /*
按秩合併x,y所在的集合
下面的那個if else結構不是絕對的,具體根據情況變化
但是,宗旨是不變的即,按秩合併,實時更新秩。
*/void union(int x, int y)
else
father[x] = y;
} }
4.並查集的重要應用:等價類的劃分 最小生成樹 kruskal 並查集
題目描述 如題,給出乙個無向圖,求出最小生成樹,如果該圖不連通,則輸出orz 輸入輸出格式 輸入格式 第一行包含兩個整數n m,表示該圖共有n個結點和m條無向邊。n 5000,m 200000 接下來m行每行包含三個整數xi yi zi,表示有一條長度為zi的無向邊連線結點xi yi 輸出格式 輸出...
Kruskal演算法(貪心 並查集 最小生成樹)
kruskal演算法 克魯斯卡爾 演算法 的高效實現需要一種稱作並查集的結構。我們在這裡不介紹並查集,只介紹kruskal演算法的基本思想和證明,實現留在以後討論。kruskal演算法的過程 1 將全部邊按照權值由小到大排序。2 按順序 邊權由小到大的順序 考慮每條邊,只要這條邊和我們已經選擇的邊不...
Kruskal演算法(貪心 並查集 最小生成樹)
2016年04月16日 17 34 35 kruskal演算法的高效實現需要一種稱作並查集的結構。我們在這裡不介紹並查集,只介紹kruskal演算法的基本思想和證明,實現留在以後討論。kruskal演算法的過程 1 將全部邊按照權值由小到大排序。2 按順序 邊權由小到大的順序 考慮每條邊,只要這條邊...