數據結構——圖解並查集(合併樹)

什麼是並查集

並查集,在一些有N個元素的集合應用問題中,我們通常是在開始時讓每個元素構成一個單元素的集合,然後按一定順序將屬於同一組的元素所在的集合合併,其間要反覆查找一個元素在哪個集合中。這一類問題近幾年來反覆出現在信息學的國際國內賽題中,其特點是看似並不複雜,但數據量極大,若用正常的數據結構來描述的話,往往在空間上過大,計算機無法承受;即使在空間上勉強通過,運行的時間複雜度也極高,根本就不可能在比賽規定的運行時間(1~3秒)內計算出試題需要的結果,只能用並查集來描述。

並查集是一種樹型的數據結構,用於處理一些不相交集合(Disjoint Sets)的合併及查詢問題。常常在使用中以森林來表示。

 實現過程

1.初始化

初始化時,每一個森林只有一個元素,他們之間是相互獨立的,其值爲-1。

若一個節點的值爲負數,則說明它是根節點,那麼他的子節點數量就爲|n+1|個,如:值爲-1的根有|-1+1|=0個子節點,值爲-6的根有|-6 +1| = 5個節點。

所有的森林一般使用一個數組來存儲,如下圖所示:

2.合併樹

在上圖中可以看到初始化時每個節點都有一個編號,從0開始,這個編號是隱藏的,其作用是爲了可以方便的在二維數組中查找到指定節點,其值爲a[ parseInt(節點編號/數組的長度) ][節點編號 % 二級數組的長度]

如7號節點的值爲

a[parseInt(7/3)][7%3] = a[2][1]  = -1

若一個節點的值爲正數,則該值表示它的上級節點的編號。現在我們來合併樹,把3號與6合併,4號與5號合併。

 可以看到6號節點的值爲3,即6號節點指向了3號節點。

相應的3號節點的值也要變化,因爲有其他樹合併到了該樹上,那麼3號節點的值就爲本來的值加上所合併上來的樹的根節點的值,即爲-1 + (-1)= -2,表示有|-2+1| = 1個子節點。

到上一步位置,現在的森林從原來的9個樹變成了7個樹。

那現在我們要合併5號節點和6號節點怎麼辦,這裏要注意一點,如果節點已經指向了其他的節點,那麼是不可以使用該節點直接進行節點,我們必須要找到該節點的根節點後,然後再對根節點進行合併。

6->3,4->5,那麼在這裏我們就是要對樹3和樹5進行合併。

a[1, 2] = a[1, 2] + a[1, 0]  //-4
a[1, 0] = a[1,2]的編號

 在合併過程中的問題:

  • 如何判斷節點是子節點還是根節點

         只需判斷該節點的值得正負情況,若爲負數則爲根節點,如果整數則爲子節點,並且它的上一級節點編號就爲該值

  • 如果判斷兩個節點A與B是否屬於同一個樹

 要判斷是否屬於同一個樹,那麼就要找到該節點的根節點,這裏使用遞歸的思想,每次找到上一級節點,如上級節點不爲根節點,則繼續向上找,知道找到根節點爲止,如果根節點的編號相同則在同一顆樹上。

/*
* num: 節點編號
* len: 數組的長度,這裏默認該二位數組的屬於爲一個正方形,如3*3
*/
function search(num){
   if(tree[parseInt(num/len)][num%len]>0)//說明是子節點
       return search(tree[parseInt(num/len)][num%len]);//不能壓縮路徑路徑壓縮
   else
       return num;
}
  • 兩棵樹A與B合併時,是A合併B,還是B合併A

      在合併樹之前,我們必須要考慮究竟是誰合併到誰上,爲什麼要進行這不操作呢,樹的深度越深,那麼查詢的效率就越慢。

當然不只是高度,在數量上也起到很大的影響作用。所以在合併時,通常是將小樹合併到大樹上。

    通過下面這個例我們就可以直觀的看出差距。

 

通過上圖可以明顯的看出吧B合併到A上所產生的影響更小。 

結束語

  • 並查集是一種常見的數據結構,應用廣泛,如迷宮遊戲
  • 歡迎訪問本人個人博客:https://www.dzyong.com

 

 

發佈了65 篇原創文章 · 獲贊 65 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章