1. 程式人生 > >圖論最小生成樹

圖論最小生成樹

前言

推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。

最小生成樹

最小生成樹(Minimum Spanning Tree),簡稱MST,更詳細點叫最小權重生成樹,是一副連通加權無向圖中一棵權值最小的生成樹。對於圖,在完全連通的情況下,則擁有生成樹。而如果圖不連通的話,則擁有多棵生成子樹,構成生成森林。

最小生成樹正式的定義為:給定一個無向圖G = (V, E)E_{ab}表示連線頂點a與頂點b的邊,有E_{ab}\in E,而w_{ab}表示該邊的權重,若存在T為E的子集(即T\subseteq E)且 (V, T) 為樹,使得

w(T)=\sum_{E_{ab}\in T}w_{ab}

w(T)最小,則此T為G的最小生成樹。

利用最小生成樹解決實際問題的例子有很多,比如某個鎮有十幾個小村莊,現要在這些村莊中構建一個通訊網路,將每個村莊都連線起來,如何走線能最節省電纜?這就可以用最小生成樹解決。

image

Prim演算法

Prim(普里姆)演算法是一種用於求解最小生成樹的演算法,通過此演算法能搜尋到圖邊集合中某個特定的子集,該子集構成的樹能連通圖的所有頂點,而且所有邊的權重之和最小。該演算法於1930年由捷克數學家沃伊捷赫·亞爾尼克(Vojtěch Jarník)發現;並在1957年由美國電腦科學家羅伯特·普里姆(Robert C. Prim)獨立發現;1959年,艾茲格·迪科斯徹再次發現了該演算法。

Prim的主要思想是:從圖中任意一個頂點開始,每次選擇與當前頂點集距離最近的頂點,將對應的邊加入到樹中,直至所有頂點被處理完。

演算法步驟

  • 對於一個加權連通圖,其頂點集合為V,邊集合為E;
  • 從集合V中任選一個頂點作為初始頂點,將該頂點標為已處理;
  • 已處理的所有頂點可以看成是一個集合T,計算所有與集合T中相連線的頂點的距離,選擇距離最短的頂點,將其標記為已處理,並記錄最短距離的邊;
  • 不斷計算已處理的頂點集合T和未處理的頂點的距離,每次選出距離最短的頂點標為已處理,同時記錄最短距離的邊,直至所有頂點都處理完。
  • 最終,所有記錄的最短距離的邊構成的樹,即是最小生成樹。

Prim過程

假如我們現在有一個7個頂點的無向加權圖,結構如下圖,分別用0-6來表示圖的每個頂點,每條邊上的數字為對應的權重。

image

為了記錄過程狀態,引入如下表格,第一列“頂點”表示圖的頂點,分別為0到6;第二列“處理標識”表示對應頂點是否已處理,未處理狀態為F,已處理狀態為T;第三列“權重”表示頂點到頂點的權重,初始值為INF;第四列“頂點”用於描述最小生成樹,與第一列的“頂點”組合成最小生成樹的邊,初始值為-1。

image

現在我們隨便選一個頂點3作為初始頂點,將該頂點標記為已處理T,並且權重置為0,因為自己對自己的權重為0。

image

此時已處理集合為{3},檢測與頂點3相鄰的頂點,先檢查(3,1)邊的權重,權重設為4,最小生成樹另外一個頂點設為3,表示頂點1到頂點3的邊。

image

繼續檢查(3,2)邊的權重,權重設為4,最小生成樹另外一個頂點設為3,表示頂點2到頂點3的邊。

image

類似地,檢查(3,4)邊的權重,權重設為5,最小生成樹另外一個頂點設為3。

image

檢查(3,5)邊的權重,權重設為1,最小生成樹另外一個頂點設為3。

image

對於已處理集合為{3},相鄰的所有頂點中,找出權重最小的邊,即權重為1的邊(3,5),將頂點5的處理標識設為T,並將其加入到已處理集合中,此時集合為{3,5}。

image

對於集合{3,5},檢測與它們相鄰的頂點,這裡注意到因為頂點3上一輪已經檢測過了,不必再針對頂點3進行權重檢測了,但頂點5能更新之前的權重(可以這樣想一下:頂點3到頂點2的權重如果為4,而頂點5到頂點2的權重為2,那麼就取權重小的)。於是,檢查(5,2)邊的權重,權重設為2,最小生成樹另外一個頂點設為5,表示頂點2到頂點5的邊。

image

接著檢測(5,3)邊,因為3的處理標識為T,屬於已處理集合內的元素,不必檢測。

image

繼續檢測(5,6)邊,權重設為3,最小生成樹另外一個頂點設為5,表示頂點6到頂點5的邊。

image

對於已處理集合為{3,5},相鄰的所有頂點中(即除了處理狀態為T的其他頂點),找出權重最小的邊,

image

所以要找的邊是權重為2的邊(2,5),將頂點2的處理標識設為T,並將其加入到已處理集合中,此時集合為{2,3,5}。

image

對於集合{2,3,5},檢測與它們相鄰的頂點,其中頂點3和頂點5上一輪已經檢測過了不必再針對它們進行權重檢測了。於是檢查(0,2)邊的權重,權重設為5,最小生成樹另外一個頂點設為2,表示頂點2到頂點0的邊。

image

繼續檢測(1,2)邊,權重設為1,最小生成樹另外一個頂點設為2。

image

接著檢測(2,3)邊,因為3的處理標識為T,屬於已處理集合內的元素,不必檢測。

image

繼續檢測(4,2)邊,此時權重為8,而上一輪檢測的權重為5,8>5,所以不更新權重,最小生成樹另外一個頂點也不更新。這種情況其實就是前面檢測頂點3到頂點4的權重更小,也就是說頂點3到頂點4更近,對於集合{2,3,5}來說,到頂點4的權重應該取更小的5。

image

接著檢測(2,5)邊,因為5的處理標識為T,屬於已處理集合內的元素,不必檢測。

image

對於已處理集合為{2,,3,5},相鄰的所有頂點中(即除了處理狀態為T的其他頂點),找出權重最小的邊,

image

所以要找的邊是權重為1的邊(2,1),將頂點1的處理標識設為T,並將其加入到已處理集合中,此時集合為{1,2,3,5}。

image

類似地,針對集合{1,2,3,5}進行檢測,找到與該集合相連的最小權重的邊,記錄下對應的邊和權重,然後將對應頂點加入到集合中。此輪找到的最小權重為3,對應的邊為(0,1),則已處理集合變為{0,1,2,3,5}。

image

繼續地,針對集合{0,1,2,3,5}進行檢測。此輪找到的最小權重為3,對應的邊為(5,6),則已處理集合變為{0,1,2,3,5,6}。

image

最後,針對集合{0,1,2,3,5,6}進行檢測。此輪找到的最小權重為3,對應的邊為(4,6),則已處理集合變為{0,1,2,3,4,5,6}。

image

此時,所有頂點都已經被處理完了,至此最小生成樹的查詢工作已經執行完畢。根據第一列的“頂點”和第四列的“頂點”,我們就能夠描述出最小生成樹了,結果如下。

image

-------------推薦閱讀------------

我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)

為什麼寫《Tomcat核心設計剖析》

我的2017文章彙總——機器學習篇

我的2017文章彙總——Java及中介軟體

我的2017文章彙總——深度學習篇

我的2017文章彙總——JDK原始碼篇

我的2017文章彙總——自然語言處理篇

我的2017文章彙總——Java併發篇


跟我交流,向我提問:

歡迎關注: