標籤:blog http 使用 io 資料 for 2014 問題
不相交集合 故名思意就是一種含有多個不相交集合的資料結構。典型的應用是確定無向圖中連通子圖的個數。其基本操作包括:
Make-Set(x):建立一個新的集合,集合的成員是x;
Union(x,y): 將包含x和y的集合合并為一個集合;
Find-Set(x): 返回指向包含x的集合的指標;
下面是一個例子,(a)是一個無向圖,(b)是使用不相交集合來找連通子圖的個數。做法是初始為各個頂點為一個集合,然後遍曆各個邊,把邊的端點的集合進行合并,當處理完所有的邊,能連通的頂點就在一個集合裡了,這樣就產生了4個集合。也就是有4個連通子圖。
如何?不相交集合呢?
最直觀和簡單的方法是用鏈表:每個集合是一個鏈表,合并時,就是把一個鏈表接到另一個鏈表的後面。由於需要支援Find-Set操作,所以,每個元素都需要有指標指向集合的前端節點。更新鏈表也需要更新這個頭指標,使其指向新集合的頭。如果我們不考慮合并時兩個集合的大小,那最壞情況下是把長的接到短的後面,這樣就需要更新更多的頭指標。而簡單的最佳化方式就是總是把短的鏈表接到長的後面。
更好的方式是用有根樹來表示。如果不做最佳化,有根樹並不比鏈錶快很多,但是使用按秩合并和路徑壓縮兩種最佳化後,可以得到跟運算元量m成線性關係的已耗用時間。
按秩合并:基本思想是跟鏈表時的最佳化類似,當兩個集合(樹)要合并時,將小的集合并到大的裡面。所採取的方法並不是記錄集合的大小,而是用秩來描述,秩是集合樹高度的一個上界。在Union時,兩個要合并的集合x,y。如果x的秩大,那就把y併入x:將y的根的父節點設定為x的根,這時x的秩是不需要改變的,因為高度沒有改變,只是增加了x的根的子節點的數量,但如何x和y的秩相同,那就隨機把某個根的父節點設定為另一個的根,但這時需要把秩增加1.
路徑壓縮:路徑壓縮是說在Find-Set時,將尋找路徑上的每個節點都直接指向其根節點,也就是把樹的高度變小了,根節點的孩子多了。但這些操作並不改變秩的大小。所以這裡的秩只是一個模糊的上界,並不真的表示樹的高度。 路徑壓縮的好處就是,在後續的Find-Set操作可以變快,中Find-Set(a)時把搜尋路徑上所有的節點的父節點都成了根節點,那後續的Find-Set(b)就可以一步完成了。
不相交集合的使用還是可以比較靈活的,遇到有環的問題,也許可以考慮,下面是勇幸|Thinking分享的一個題目:
A zero-indexed array A consisting of N different integers is given. The array contains all integers in the range [0..N−1]. Sets S[K] for 0 ≤ K < N are defined as follows: S[K] = { A[K], A[A[K]], A[A[A[K]]], ... }. Sets S[K] are finite for each K.
Write a function:
class Solution { public int solution(int[] A); }
that, given an array A consisting of N integers, returns the size of the largest set S[K] for this array. The function should return 0 if the array is empty.
這個問題也是比較適合用這個不相交集合這種資料結構來解決的。