1. 程式人生 > >並查集的一些個人觀點 以及克魯斯卡爾演算法的詳解

並查集的一些個人觀點 以及克魯斯卡爾演算法的詳解

先丟擲個問題,什麼是並查集,它有什麼用?

看我這篇Blog的人想必就為了弄明白,下面寫出我個人的一些觀點。

1 什麼是並查集,以及並查集要完成的目標。

舉個例子,通火車要修路,已經修了一部分了,但各個地方零零散散的沒有統一成一個整體的鐵路網,中國這麼多地方,我不能每個地方都直接聯絡,這樣花費的代價也太大了。所以我們這樣想,只要從一個地方能去中國任意一個地方就好了,用不著每個地方都相互有專門的鐵路。這就衍生了一個問題,我怎麼知道我要去的地方在不在我這個鐵路網裡?我該不該修到這兒的鐵路?不好判斷是吧,這就用到了並查集思想。

並查集主要用在判斷一個圖中的兩個頂點是否能相聯通的問題。

2 實現並查集的思想。

既然頂點與頂點能直接相同,那麼他們一定處於同一張連通網中。因此,我們可以把不同的連通網分別看成一個個集合。我們要判斷兩點是否相通,可以檢索這兩點是否在同一張連通網裡,在就能相通,反之不能。

所以,我們應該怎樣設計,使得我們能判斷兩點是否在同一張網裡是問題的關鍵。

這時,我們自然而然的想到了,如果每一個點都能用這個網中的固定點表示(姑且把這個點稱為BOSS),那麼問題就解決了。例如下圖,我們要判斷A和B是否能相通,A找啊找找到A的BOSS是BOSS1,B也找啊找,找到B的BOSS也是BOSS1。說明他們位於同一張聯通圖中,即他們能過去。

但是這找BOSS還是好麻煩,不好找,如果是兩棵樹的話就好多了,直接以根節點作為所有節點的BOSS,每棵樹只需要知道它自己的上級是誰,一層一層往上找,最終就找到BOSS了。例如: 要找G和B是否位於同一張網,G開始找BOSS,G的上級是D,D的上級是A,A就是BOSS,返回G的BOSS為A。同理,B找到BOSS也是A。說明他們位於同一張網中。再例如我要判斷E和N是否能相通,我就找E的BOSS,再找N的BOSS,明顯A不等於H。所以他們不能連通。

總而言之,並查集就是把每堆元素合併為一個具有相同BOSS的集合,如果兩堆元素BOSS不同,說明他們不連通,反之連通。

3 例項程式碼實現

我認為主體分為四個部分

(1)建立並查集(用陣列也好,連結串列也行),我就用陣列舉個例子

int parent[maxSize];

這裡下標代表頂點編號,元素代表它的上級(就是通過上級找上級.....最終能找到它的BOSS)。

(2)初始化並查集

for(int i  = 0 ; i < N ;++i) 

{

parent[i] = i;

}

這裡的N代表頂點的個數,首先預設每個頂點都不能互相聯絡,每一個頂點自己就是一個集合,故a[i] = i;它的上級就是它自己,它就是BOSS。

(3)構造一個查詢BOSS的演算法,我用迭代的方式給出(遞迴也能寫)

int getBoss(int a)

{

while(a != parent[a])  a = parent[a];

return a ;

}

while迴圈的意思的如果傳進來的a已經是BOSS,直接輸出,否則進行查詢([注]根據存放的值可以找到它上級,它的上級又繼續查詢它上級的上級,直到a==parent[a]說明找到了,返回a)

(4)判斷將頂點合併進同一個集合(有同一個BOSS),我用迭代的方法做個例子

void merge()

{

int a , b;

a = getBoss(c);

b = getBoss(d);

   if(a != b)

   {

      parent[a] = b;

   }

}

c,d代表傳進來的頂點元素下標,如果兩個BOSS不相等,說明不在同一棵樹中,即不會產生迴圈,把b歸於a的集合,從此他們就在一棵樹中,共同擁有一個BOSS

以上程式碼也可以作為模板。

舉個應用的例子

此段程式碼是克魯斯卡爾演算法求最小生成樹

最後舉出一些實際應用的例子

整幅圖的連通性問題。比如隨意給你兩個點,讓你判斷它們是否連通,或者問你整幅圖一共有幾個連通分支,也就是被分成了幾個互相獨立的塊。問還需要修幾條路,實質就是求有幾個連通分支。等等。。

有什麼不對的,歡迎指正!

註釋很詳細了已經,關於並查集的壓縮路徑下次再說,寫不動了啊!!!