在一個有權連通圖(網絡)中,生成樹的各邊權值之和稱為生成樹的代價。在網絡的所有生成樹中,權值最小的那顆生成樹稱為最小代價生成樹(Minimum Cost Spanning Tree),簡稱為最小生成樹Minimum Spanning Tree)。討論問題就是如何尋找一顆各邊權的總和最小的生成樹。
Q:假設n個城市之間建立公路網,則連通n個城市只需要n-1條公路。如何在最節省經費條件下構建此公路網。對於諸如此類問題都可以使用一個具有n個頂點的無向網絡來表示。由於網路的每個生成樹正好是n-1條邊,這樣的問題就轉化為如何選擇n-1條邊使它們形成一個最小生成樹。
目前,采用貪心思想來求解最小生成樹的方法主要有Kruskal(克魯斯卡爾算法)和Prim(普里姆算法)
Kruskal算法
是一種按照網絡中各邊的權值遞增的順序構造出 最小生成樹的方法。其基本思想是設無向連通網為G=(V,E),令G的最小生成樹為T,其初態為T(V,{}),即開始時,最小生成樹T是由圖G中的n個頂點構成,頂點之間沒有一條邊,這樣T中各個頂點各自構成一個連通分量。然后按照邊的權值由小到大的順序,考察G的邊界E的各條邊。並選擇權值最小且不與T中的邊構成環的一條邊加入到最小生成樹的邊集合中(這一條是Kruskal算法所使用的貪心選擇准則),如此下去,直到所有的結點都加入到最小生成樹的結點集合中為止,Kruskal算法求解過程如下。
1)初始化
將圖的邊按權值大小進行排序。通常使用最小堆來存放圖中的所有邊,堆中的每個結點的內容包括一條邊的起點、終點和代價。
2)求代價最小的邊
在構造最小生成樹過程中,利用並查集的運算檢查依附一條邊的兩頂點是否在同一連通分量(即並查集的同一個子集合)上,如果是則舍去這條邊;否則將此邊加入T中,同時將這兩個頂點放在同一個連通分量上。
並查集作為一種高效易實現的數據結構提供看集合間的合並操作,即將兩個並查集合並成一個並查集;同時提供了集合元素的歸屬查找操作,即返回待查找元素所在集合名字。
最常見的並查集實現是森林,即在森林中,每棵樹代表一個集合,用樹根來標識一個集合。當要把兩個集合S1和S2合並時,只需將S1的根的父親設置為S2的根就可以。當查找一個元素X時,只需要沿着葉子到根節點的路徑找到X所在樹的 根節點,就確定了X所在的集合。
3)合並
隨着各邊逐步加入到最小生成樹T的邊集合中,各連通分量也逐漸合並,直到形成一個連通分量為止。
Prim算法
基本思想是設無向連通圖G=(V,E),令G的最小生成樹為T=(U,TE),其中U是G的生成樹的頂點集合,TE是G的生成樹中邊的集合。開始時,U={u0},TE={}。u0是G中的任意一個頂點。然后重復進行如下操作:在一個頂點在U中,另一個頂點不在U中的所有邊中選擇權值最小的邊(u,v),把它的頂點加入到集合U中(這是prim算法所使用的貪心算法)。如此繼續下去,直到網絡中的所有頂點都加入到生成樹頂點集合U中為止,Prim算法的求解過程如下:
1)初始
T只包含根結點s,U={s}。對於任意結點v∈V-U,令 closedge[v].lowcost=cosr(s,v) , closedge[v].adjvex=s.
2)選擇
選擇結點u∈V-U,使closedge[u].lowcost=min{closedge[v].lowcost | v∈ V-U}。將結點u及邊< closedge[u].adjvex,u>加入數T,令U=U ∪ {u}。
3)修改
對於任意結點v∈V-U,如果cost[u,v] < closedge[v].lowcost,則closedge[v].lowcost=cost[u,v];closedge[v].adjvex=u.
4)判斷
若U=V,則算法結束,否則轉第2)步。
其中,數組closedge表示的是一個從頂點集U到V-U的代價最小的生成樹,數組cost代表兩個頂點之間的代價。