圖解:如何實現最小生成樹(Prim算法與Kruskal算法)

這是圖算法的第四篇文章 圖解:如何實現最小生成樹算法

文章目錄:數組

  • 1.概念和性質
  • 2.思路探索
  • 3.Kruskal算法
  • 4.Prim算法
  • 5.代碼實現

1.概念和性質

今天咱們考慮的模型是加權無向圖,問題是如何獲取它的一幅最小生成樹!首先,咱們給出最小生成樹的定義:微信

圖的生成樹是它的一棵含有其全部頂點的無環連通子圖。一幅加權圖的最小生成樹(MST)是它的一棵權值(樹中全部邊的權值之和)最小的生成樹。數據結構

如圖所示:code

一顆最小生成樹(黑色部分)

首先,咱們給出一些約定來簡化問題(這並不會影響咱們理解問題)對象

  • 只考慮連通圖(若是不連通的話是不存在最小生成樹的)
  • 邊的權重多是0或者負數
  • 全部邊的權重各不相同(咱們給出這個假設以後對於一幅圖來講只存在惟一的最小生成樹,這樣方便咱們理解,可是若是把這個限制條件去掉,咱們以前獲得的算法依然有效😎)

簡而言之,咱們的討論對象是一幅權重各不相同的加權無向圖,任務是求最小生成樹的每條邊。接下來,咱們一塊兒思考如何實現這個算法!blog

2.思路探索


1)咱們的任務是求得最小生成樹的每條邊,也就是我只要肯定了這些邊,天然而然也就惟一肯定了最小生成樹;那麼,一棵最小生成樹有多少條邊呢?咱們先來回顧一下的兩個性質:排序

  • 用一條邊鏈接樹中的任意兩個頂點都會產生一個新的環
  • 從樹中刪去一條邊將會獲得兩棵獨立的樹

由於一共有V個頂點,生成樹的邊剛好鏈接全部頂點(很少很多),因此生成樹一定有V-1條邊。好了,恭喜你!🙃到這裏咱們已經前進了一小步!索引

2)接下來,我說一句話你看是否是對的:隊列

只要找到屬於最小生成樹的V-1條邊(不管什麼手段),也就解決了這個問題

你確定會說,這不是顯然的嗎?對!但這個是咱們向前走的基本思想。記牢了🤓

3)接下來,咱們就要尋找一種條件,只要一條邊知足這個條件,那麼咱們就可以斷言這條邊確定在最小生成樹中。一旦存在,咱們就能夠經過創造這種條件找到屬於最小生成樹的邊,將它標記;反覆創造上述條件,就能夠反覆地標記邊;直到咱們標記了V-1條邊! 你是否是驚奇地發現:咱們已經成功地解決了這個問題!

4)咱們的任務變成了如何尋找與製造這種條件。站在巨人的肩膀上,咱們只須要理解就好了😂。這個條件,咱們稱之爲切分定理

6)咱們隨意將一幅加權圖分爲兩個非空的集合,橫跨兩個集合的一條邊被稱爲橫切邊。而橫切邊中最短的那個一定屬於最小生成樹! 如圖所示:

全部的灰色頂點是一個集合,全部的白色頂點是另一個集合,橫跨兩個集合的全部的邊稱爲橫切邊(用紅色標出);其中權重最小的橫切邊一定屬於最小生成樹!

這個定理其實很容易想明白,由於這兩個集合之間必需要存在且只能存在一條邊;若是存在的不是這條最短橫切邊,那它就不是最小生成樹了!請注意一條很重要的性質:這種劃分是任意的!
——————咱們隨便怎麼劃分都無所謂!這就爲處理問題帶來了很強的靈活性!

7)咱們隨後介紹一種通用的算法思想:貪心算法(其實咱們在前文已經接觸過)。

使用切分定理找到最小生成樹的一條邊,不斷重複直到找到最小生成樹的全部邊V-1條便可)

這是一幅貪心算法的執行過程,每次都是將全部頂點分紅兩個集合(注意:這是任意的!!),而後取最小的橫切邊加入最小生成樹。如此反覆,最終就達到了咱們的目的!

8)接下來再日後,咱們將真正地實現這種算法,而不是隻停留在思想上(talk is cheap,show me the code!😍)

  • Kruskal算法
  • Prim算法地兩種形式

3.Kruskal算法

1)咱們接着剛纔的思路繼續,咱們面臨着兩個問題:切分找到最小橫切邊;對於切分,因爲是隨意的,因此仍是很容易實現的;另外一個問題:怎樣很天然地找到最小的橫切邊呢?

2)一個比較容易想到的解決方案是:

按照邊的權重順序(從小到大)處理他們;
首先,將最小的邊加入最小生成樹,而後從小到大依次加入邊,(注意:待加入的邊不能和已經加入的邊構成環);一直重複上述過程直到樹中含有V-1條邊爲止

3)如今咱們仔細思考一下這個方法。只要待加入的邊和已經加入的邊沒有構成環,就說明這條邊是一條橫切邊,同時,得益於咱們加入的順序(從小到大),咱們能夠肯定這條待加入的邊是最小橫切邊,那麼它必定屬於最小生成樹,將它加入也就沒有什麼毛病了。來看一下下面的過程:

咱們在實現的時候使用了一條優先隊列來將邊按照權重排序;用前幾篇文章中實現的判斷無向圖連通性的類判斷是否連通;用一條隊列來保存最小生成樹的全部邊;就能夠很容易實現Kruskal算法

4.Prim算法

Prim算法的思想與Kruskal算法乍一看有所不一樣,可是最終你會發現,只是在尋找最小權重的橫切邊這裏使用了不一樣的 策略罷了。

1)咱們考慮這樣一種方案:維護一棵生長中的樹

  • 初始化:將一個頂點(隨意,記爲A)添加到最小生成樹中
  • 找到與最小生成樹相連的權重最小的一條邊(一個頂點在樹中,一個頂點不在),並將這條邊和相應的頂點加入到最小生成樹中
  • 重複上述操做,直到全部頂點都被添加

2)這個過程比Kruskal更加簡明——————與最小生成樹相連的權重最小的一條邊;咱們把已經在樹中的點記爲集合A,全部沒有在樹中的頂點記爲集合B,顯然咱們選擇的那條邊就是最小橫切邊!

3)雖然過程比較簡明,可是實現的代碼卻不簡單,咱們須要:

  • 頂點。使用一個由頂點索引的布爾數組 marked[],若是頂點 v 在樹中,那麼 marked[v] 的值爲true
  • 邊。使用一條隊列mst來保存最小生成樹中的邊
  • 橫切邊:使用一條優先隊列 MinPQ<Edge> 來根據權重比較全部邊

具體的操做過程以下:

4)到這兒,咱們已經實現了Prim算法,它被稱爲 Prim算法的延時實現 ;可是,還有能夠改進的地方,咱們能夠經過一些改進————刪除一些冗餘的信息,從而獲得Prim算法的即時實現

5)可是,在這篇文章中,咱們就不加以介紹了;它的思想和延時實現一致,目標一樣是找到與最小生成樹相連的權重最小的一條邊,只不過實現方法更加靈活罷了!

5.代碼實現

此次和往常不一樣,我並無在文章中間摻雜算法的具體實現,個人想法是把它真正的思路講明白(但願我講的沒有讓你失望🤔);可是,代碼仍是要有的,我將它們放在了這裏,具體來講書籍算法4上面都有!!

  • 如何實現一個加權邊
  • 如何實現加權無向圖
  • Kruskal算法的實現
  • Prim算法的實現

如何實現一個加權邊

如何實現加權無向圖

Kruskal算法的實現

Prim算法的實現(兩張圖片)

6.後記

碼字繪圖不易,若是以爲本文對你有幫助,還請不要白嫖,關注、點贊、在看都是對小超創做的一種承認!

歡迎你們關注個人公衆號:小超說 ,以後我會繼續創做算法與數據結構以及計算機基礎知識的文章。也能夠加我微信chao_hey(備註:職業-城市) ,咱們一塊兒交流,一塊兒進步!

本文參考:《算法》(第四版),圖片來源於此

相關文章
相關標籤/搜索