1. 程式人生 > >並查集的兩種優化(按秩合併,路徑壓縮)

並查集的兩種優化(按秩合併,路徑壓縮)

並查集是建立在對不相交集合進行的兩種基本操作的基礎之上的。操作之一:檢索某元素屬於哪個集合;操作之二:合併兩個集合。黑書上說了,這種結構顯然可以用連結串列或森林實現,顯然用連結串列進行查詢時間複雜度應該是O(n)級別的,而使用森林進行查訪如果處理的好時間複雜度就應該是O(logn)。對於用森林來實現並查集,黑書上有句加點話我同樣認為很重要“用樹根來標識一個集合”。於是對於並查集就存在這麼兩個很關鍵的優化操作:第一,讓深度小的數成為深度較大的樹的子樹,這個優化成為啟發式合併,這樣做之後樹的深度為O(logn);第二,找到u所在的樹根v以後,把從u到v的路徑上所有點的父親都設定為v,這個優化稱作路徑壓縮。既然第一個優化叫做啟發式合併,那麼很重要的就是啟發式函式的選取,一般可以用樹的深度作為啟發函式值,但由於路徑壓縮優化,樹的深度是在不斷變化的。這時候便可引入一個秩,按我的理解就是一顆樹收取與其等秩的樹的次數,因為合併的最底層操作就是兩個獨立元素的合併,所以rank越大,樹中的元素越多,顯然rank的初值都是0。

1) 按秩合併:

按秩合併的基本思想是使包含較少結點的樹德根指向包含較多結點的樹的根,而這個樹的大小可以抽象為樹的高度,即高度小的樹合併到高度大的樹,這樣資源利用更加合理。
     為了實現一個按秩合併的不想交集合森林,要記錄下秩的變化。對於每個結點x,有一個整數rank[x],它是x的高度(從x到其某一個後代葉結點的最長路徑上邊的數目)的一個上界。(即樹高)。當由MAKE-SET建立了一個單元集時,對應的樹中結點的初始秩為0,每個FIND-SET操作不改變任何秩。當對兩棵樹應用UNION時,有兩種情況,具體取決於根是否有相等的秩。當兩個秩不相等時,我們使具有高秩的根成為具有較低秩的根的父結點,但秩本身保持不變。當兩個秩相同時,任選一個根作為父結點,並增加其秩的值路徑壓縮。

2)路徑壓縮:

是在FIND-SET操作中,把查詢路徑上的每個結點都直接指向根結點。路徑壓縮並不改變結點的秩。關於路徑壓縮,看圖理解,之間為FIND-SET操作前集合,之後為FIND-SET操作後集合。此時,查詢路徑上的每一個結點都直接指向根。
路徑壓縮程式碼實現方式有兩種:遞迴式和非遞迴式。
1)遞迴方式
虛擬碼:
FIND-SET(x)


1  if x ≠ p[x]


2     then p[x] ← FIND-SET(p[x])


3  return p[x]