圖論算法之最小生成樹(Krim、Kruskal)

圖論算法之最小生成樹(KrimKruskal

1、圖論基礎

圖論 (Graph theory) 是數學的一個分支,圖是圖論的主要研究對象。 圖 (Graph) 是由若干給定的頂點及連接兩頂點的邊所構成的圖形,這種圖形通常用來描述某些事物之間的某種特定關係。頂點用於代表事物,連接兩頂點的邊則用於表示兩個事物間具有這種關係。

  • 圖的相關名詞解釋:

    • 圖的表示法:G=(V(G),E(G)),其中V(G)表示點集,E(G)表示邊集;

    • 度:與一個頂點v關聯的邊的條數稱作該頂點的度,如果是有向圖的話,分爲入度和出度。

  • 圖的分類:有向圖和無向圖;是否爲加權圖;有環圖和無環圖。

  • 圖的表示法:鄰接矩陣和鄰接表。(通常稀疏的圖用鄰接表,稠密的圖用鄰接矩陣,稀疏的圖用鄰接矩陣會非常浪費空間的)

  • 圖的遍歷:深度優先搜索和廣度優先搜索。(DFS使用棧,將頂點存儲在棧中,頂點是沿着路徑被探索的,存在新的相鄰節點就去探索;BFS使用隊列,將頂點存入隊列中,最先如隊列的頂點先被探索)

  • 圖遍歷作用:可以用來尋找特定的頂點或尋找兩個頂點之間的路徑,檢查圖是否連通,檢查圖是否含有環等。

  • 圖遍歷的思想:追蹤每個第一次訪問的節點,並且追蹤有哪些節點還沒有被完全探索。對於兩種遍歷方法,都需要指明第一個被訪問的頂點。完全探索一個頂點要求我們查看該頂點的每一條邊。爲了保證算法的效率,務必訪問每個頂點至多兩次。

2、最小生成樹

最小生成樹(Minimum Spanning TreeMST:無向連通圖中邊權和最小的生成樹(最短路徑連接所有節點)。

注意:只有連通圖纔有生成樹,而對於非連通圖,只存在生成森林。

最小生成樹有很多實際問題的應用,例如:要在n個城市之間鋪設光纜,主要目標是要使這n個城市的任意兩個之間都可以通信,但鋪設光纜的費用很高,且各個城市之間鋪設光纜的費用不同,因此另一個目標是要使鋪設光纜的總費用最低。這就需要找到帶權的最小生成樹。

有兩個經典的算法可以用來解決最小生成樹問題:Kruskal算法和Prim算法。其中Kruskal算法中便應用了並查集這種數據結構。Prime算法維護的是頂點的集合,而Kruskal維護的是邊的集合。
在這裏插入圖片描述

3、Kruskal算法

此算法可以視爲「加邊法」,即把所有cost從小到大排序,然後使用並查集依次嘗試合併每個邊:

  • 如果合併成功,則加入這條邊;
  • 如果合併失敗(邊的兩個節點已經合併過),說明產生了環,則丟棄這條邊。

Kruskal算法 = 貪心 + 並查集

通過並查集合並後,每個連通分量節點都會有相同的root,因此檢查所有節點的root

  • 如果檢查到只有一個root,說明這個圖只有一個連通分量,是連通圖,返回cost
  • 如果檢查到超過一個root,說明這個圖有多個連通分量,不是一個連通圖,返回-1

在這裏插入圖片描述

4、Krim算法

此算法可以視爲「加點法」,即Kruskal算法每次添加一個最小的邊,而Prim算法則是每次添加一個距離已選取節點集最近的點。

流程如下:

  • 集合S表示已選取的節點集;
  • 選任意一個節點作爲起始節點 A,放到集合S中,並更新其他節點到集合S的最近距離。因爲當前S中只有一個節點 A,因此更新爲到節點 A 的距離;
  • 選取距離S最近的一個節點 B,放到集合S中,並更新其他節點到集合S的最近距離。也就是節點 i 的距離更新爲 min { adj[A][i], adj[B][i] }
  • 繼續選取、更新,直到N個節點都被選取。
    在這裏插入圖片描述

實際提交發現,Prim算法效果遠不如Kruskal好,原因如下:

  • 題目給的是邊(connections),而使用Prim算法,需要快速得到兩個節點之間的距離。如果每次都直接遍歷connections,複雜度太高,因此需要先轉換成鄰接矩陣或鄰接表。選擇合適的鄰接矩陣或鄰接表,是解決本題的一個關鍵。
  • 另外一個關鍵點就是,獲取距離最小的節點,可以直接遍歷,也可以藉助 PriorityQueue 實現。