算法導論——用於不相交集合的數據結構
不相交集合的操作
一些應用涉及將n個不同元素分成一組不相交的集合,常進行兩種操作:尋找包含制定元素的唯一集合以及合並兩個集合。操作進行以下定於:
MAKE-SET(x)建立一個新的集合,僅含有x
UNION(x,y)將包含x和y的兩個集合合並成一個新的集合,刪除原本的集合
FIND-SET(x)返回一個指向包含唯一x的集合的指針
無向圖的連通分量就是一個例子。
對於如圖所示的4個連通分量,先對每一個單獨的點建立一個單獨的集合,然後依次根據每條邊合並對應的集合,最後形成4個不相交的集合
1 CONNECTED-COMPONENTS(E,V){//有E[n]個頂點V[m]條邊2 for(i=0;i<n;i++) 3 MAKE-SET(V[i]); 4 for(i=0;i<m;i++){ 5 if(FIND-SET(V[i].u != FIND-SET(V[i].v)) 6 UNION(V[i].u,V[i].v); 7 } 8 }
不相交集合的鏈表表示
(a)所示的結構通過不相交集合各自形成一個鏈表,索引頭部包含鏈表的頭部和尾部,鏈表各個結點包含指向索引頭部和下個結點兩個指針。對於這種結構,MAKE-SET(x)和FIND-SET(x)都可以在O(1)的時間完成。
(b)展示了UNION(x,y)的操作,需要將另一個鏈表連接到一個鏈表的末尾,然後修改索引結點的尾部,同時被合並的鏈表每個結點指向的索引頭部都要修改,所以該操作至少要花費O(Y)的時間
為了減少合並的時間,可以在頭部額外存儲每個鏈表的大小,這樣可以將小的集合合並到大的集合中去。
不相交集合森林
使用有根樹來表示集合,書中每個節點包含一個成員,一棵樹代表一個集合,每個成員都指向他的父結點,根結點的父結點指向自己。
(b)是合並操作,將c的父親改為f即可。這種不相交集合森林的結構MAKE-SET和UNION的效率很高,但FIND-SET收到樹高度的影響,效率較低。因此,有兩種改進的啟發式算法。
第一種是按秩合並,對於每個結點記錄他的高度,將秩較小的結點的父親改為大的那個。若兩邊相等,則任選一個作為父親,根結點的秩加一。
第二種是路徑壓縮,每次調用FIND-SET的時候,將查找路徑上的結點的父親直接改為根結點。
1 MAKE-SET(x){ 2 x.p = x; 3 x.rank = 0; 4 } 5 6 UNION(x,y){ 7 if(x.rank > y.rank) 8 y.p = x; 9 else{ 10 x.p = y; 11 if(x.rank == y.rank) 12 y.rank++; 13 } 14 } 15 16 FIND-SET(x){ 17 if(x != x.p) 18 x.p = FIND-SET(x,p); 19 return x.p; 20 }
算法導論——用於不相交集合的數據結構