並查集 啓發式合併詳解 + C代碼實現

並查集 啓發式合併

並查集,這是個比較簡單的東西,用森林模擬集合,集合合併則是兩顆樹的合併,查找集合則是尋找某棵樹的根節點

因爲不可避免的會遇到各種合併操作,,所以可能導致某棵樹的深度較大,,對應的則出現了並查集的路徑壓縮還有啓發式合併

這裏不將路徑壓縮,主要是這個東西比較普及了,而且理解起來不難。

所以直接進入正題:啓發式合併

啓發式合併

第一次看到這個名稱,實在劉汝佳的書上,當時還驚訝,並查集寫了一年了,還真不知道這個啓發式合併,接着看,發現實現是使用了一個rank數組,,,原書這麼說的:

一個優化是:把小的樹合併到大樹中,這樣會讓深度不太大。這個優化稱爲啓發式合併。rank[i] 表示 i 的秩,並用秩來代替深度做剛纔提到的啓發式合併。

然後就懵了,秩這個東西怎麼理解……

於是上網尋找,發現很多blog所描述的都和書上幾乎一模一樣。。

所以自己想辦法理解,,還好理解了

數組rank的意義

這裏舉個例子

假設我們把這些樹看成——一個個城市,而這些城市,因發展的程度被分爲不同等級,比如小型城市,中型城市,大型城市,城市與城市之間可以合併,並且規定,小型城市和小型城市合併,其中一個城市會升級爲中型城市,兩個中型城市合併,其中一個城市會升級爲高級城市

我們再次做一個更嚴密的規則:

  1. 只有兩個同級城市合併,其中一個城市才能升級
  2. 小型城市合併到大型城市中,大型城市不升級
  3. 等級高的城市必須由等級低的城市組成

回到樹——這個問題

我們假設rank[a]表示樹a的等級,姑且這麼稱呼吧,叫做名稱也可以。根據上面的規則(轉換一下),如果一棵樹的等級較高,則這棵樹的”規模“會比較高,也就是深度會深一些,根據之前的合併優化規則:

一個優化是:把小的樹合併到大樹中,這樣會讓深度不太大。

也就是把等級較低的樹合併到等級較高的樹上

總結

我們寫一個合併函數,這個函數需要根據兩棵樹的等級(rank)來判斷哪棵樹合併到另一棵樹,並且判斷一棵樹是否需要升級

代碼:

void union_ (int a,int b) {
    int x = find_(a), y = find_(b);
    if (ranks[x] > ranks[y]) {//兩棵樹一定不同級,不存在升級操作
        bcset[y] = x;
    } else {
        bcset[x] = y;
        if (ranks[x] == ranks[y]) {//兩棵同等級的樹合併,其中一棵樹升級
            ranks[y] ++;
        }
    }
}
發佈了29 篇原創文章 · 獲贊 3 · 訪問量 5739
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章