1. 程式人生 > >最小生成樹的兩個演算法之二:kruskal演算法

最小生成樹的兩個演算法之二:kruskal演算法

基本概念

樹(Tree):如果一個無向連通圖中不存在迴路,則這種圖稱為樹。
生成樹 (Spanning Tree):無向連通圖G的一個子圖如果是一顆包含G的所有頂點的樹,則該子圖稱為G的生成樹。
生成樹是連通圖的極小連通子圖。這裡所謂極小是指:若在樹中任意增加一條邊,則將出現一條迴路;若去掉一條邊,將會使之變成非連通圖。
最小生成樹(Minimum Spanning Tree,MST):或者稱為最小代價樹Minimum-cost Spanning Tree:對無向連通圖的生成樹,各邊的權值總和稱為生成樹的權,權最小的生成樹稱為最小生成樹。
構成生成樹的準則有三條:
<1> 必須只使用該網路中的邊來構造最小生成樹。
<2> 必須使用且僅使用n-1條邊來連線網路中的n個頂點
<3> 不能使用產生迴路的邊。
構造最小生成樹的演算法

主要有:克魯斯卡爾(Kruskal)演算法和普利姆(Prim)演算法他們都遵循以上準則。

Kruskal演算法(並查集+貪心)

Kruskal演算法的基本思想是以邊為主導地位,始終選擇當前可用(所選的邊不能構成迴路)的最小權植邊。所以Kruskal演算法的第一步是給所有的邊按照從小到大的順序排序。接下來從小到大依次考察每一條邊(u,v)。
具體實現過程如下:
<1> 設一個有n個頂點的連通網路為G(V,E),最初先構造一個只有n個頂點,沒有邊的非連通圖T={V,空},圖中每個頂點自成一格連通分量。
<2> 在E中選擇一條具有最小權植的邊時,若該邊的兩個頂點落在不同的連通分量上,則將此邊加入到T中;否則,即這條邊的兩個頂點落到同一連通分量上,則將此邊捨去(此後永不選用這條邊),重新選擇一條權植最小的邊。
<3> 如此重複下去,直到所有頂點在同一連通分量上為止。

把所有邊排序,記第i小的邊為e[i] (0<=i<m, m為邊的個數) 
初始化MST為空 (MST為裝載邊的容器,選中的邊會新增進MST)
初始化連通分量,使每個點各自成為一個獨立的連通分量(用並查集實現)
for (int i = 0; i < m; i++){
    if (e[i].u和e[i].v不在同一連通分量){
       把邊e[i]加入MST
       合併e[i].u和e[i].v所在的連通分量 
    } 
} 

並查集只需要一個parent陣列、rank陣列,和兩個函式:合併、查詢來構成。與之前介紹的有點細微不同的時,我們的parent陣列進行了一個小小的改進。在

原先的並查集介紹中,對於根節點,它的父節點就是本身,這樣查詢時可以判斷該節點是否為根節點,但這個根節點的父節點本身的值沒什麼意義。這樣很浪費資源,我們可以在保證查詢操作能辨別它是根節點的同時,用根節點的父節點來儲存更有用的資訊!於是我們規定:對於所有的根節點,他們的父節點值是個負值,且負值的絕對值大小代表該樹的節點數量。負值使得根節點和非根節點有所區別,而負值的大小則儲存了樹節點總數這一個很有用的資訊。