图论(九)最小生成树-Kruskal算法
前面說過,Kruskal是從最短邊著手構建最小生成樹的。其基本過程是:先對圖中的所有邊按照權重值從小到大進行排序,然后著手選取邊構建最小生成樹。如果直接從小到大按順序選取,有可能形成了環,所以對環的處理就成了核心問題。
我們還是以前面的鄉鎮假設光纖網絡為例:
Kruskal算法工作步驟如下:
(1)?將邊進行排序。
| Begin | End | Weight |
| e | i | 7 |
| c | h | 8 |
| a | b | 10 |
| a | f | 11 |
| b | g | 12 |
| b | h | 12 |
| d | i | 16 |
| f | g | 17 |
| b | d | 18 |
| g | i | 19 |
| d | e | 20 |
| d | h | 21 |
| c | d | 22 |
| d | g | 24 |
(2)?按權重依次從小到大將邊加入最小生成樹。
?
我們依次將邊e-i(7),c-h(8),a-b(10),a-f(11),b-g(12),b-h(12),d-i(16)加入最小生成樹中(圖中標綠色的邊),到目前為止,沒有任何問題。
(3)?檢查新加入的邊是否構成了環。
下面,按照順序需要將邊f-g(17)加入最小生成樹。
?
很不幸,這時a-b-g-f-a構成了環。于是f-g(17)被排除,不能被加入最小生成樹。
下面按順序加入的邊是b-c(18),同樣b-c-g-b也構成了環,所以邊b-c(18)也被排除。如此依次類推,最后得到的最小生成樹是:
?
好了,算法介紹完了。就這么簡單!
?
當然,這里有個核心問題還沒有清楚的描述,即怎樣判斷構成了環?
判斷原理也很簡單:最小生成樹加入一條邊從而構成環,那么這條邊的兩個頂點除了這條邊外,必然還有另一條路徑,即這兩個頂點在加入這個邊之前,就是連通的,或者在一個連通子圖上。
因此,在G=(V,E)中,我們令最小生成樹的初始狀態為只有n個頂點,0條邊的非連通圖T=(V,{}),圖中每個頂點都是一個連通子圖,即有n個連通子圖。依次從E中按順序從小到大選擇邊,若該邊的兩個頂點落在T中不同的連通子圖上,則將次變加入到T中,否則舍去此邊,依次類推,直到T中所有頂點都在同一個連通子圖上為止。
光說不練假把式。上代碼:
# -*- coding: utf-8 -*-
# 構建圖G,使用了鄰接矩陣表示法,9999代表非直連
G =[[0,10,9999,9999,9999,11,9999,9999,9999], ?# a的鄰居表
?????[10,0,18,9999,9999,9999,12,12,9999], ?# b的鄰居表
?????[9999,18,0,22,9999,9999,9999,8,9999], ?# c的鄰居表
?????[9999,9999,22,0,20,9999,24,21,16], ?# d的鄰居表
?????[9999,9999,9999,20,0,26,9999,9999,7], ?# e的鄰居表
?????[11,9999,9999,9999,26,0,17,9999,9999], ?# f的鄰居表
?????[9999,12,9999,24,9999,17,0,9999,19], ?# g的鄰居表
?????[9999,12,8,21,9999,9999,9999,0,9999], ?# h的鄰居表
?????[9999,9999,9999,16,7,9999,19,9999,0]] ?# i的鄰居表
# 構建頂點序號與符號對應集
v_dict ={}
v_strdict ={}
v_str ='abcdefghi'
for iin range(len(G)):
????v_dict[i]= v_str[i]
for i, valuein enumerate(v_str):
????v_strdict[value]= i
# 構建邊集
E =[(v_dict[u], v_dict[v], G[u][v]) for uin range(len(G))for vin range(len(G[u]))if 0< G[u][v]< 9999and u< v]
# 對邊集進行排序
E =sorted(E,key=lambdax:x[2])
# 構建最小生成樹
T =set()
# 構建頂點連通子圖
V =[0]* len(G)
# 判斷是否屬于同一連通子圖
def find_connect(V,v):
????whileV[v]> 0:
????????v= V[v]
????returnv
# 循環邊
for ein E:
????#邊的兩個頂點是否已連通
????v1, v2= find_connect(V, v_strdict[e[0]]), find_connect(V, v_strdict[e[1]])
????ifv1 !=v2:
????????V[v1]= v2
????????T.add(e) #將邊加入最小生成樹
# 輸出最小生成樹
print(T)
# 輸出代價
print(sum([t[2]for tin T]))
?
輸出結果:
{('e', 'i', 7), ('g', 'i', 19), ('c', 'h', 8), ('a', 'f', 11), ('b', 'g', 12), ('d', 'i', 16), ('a', 'b', 10), ('b', 'h', 12)}
95
該算法的主要執行過程在最后的for循環處,find_connect函數與頂點的個數V決定,時間復雜度為O(logV),而外面是邊E的循環,因此Kruskal算法的時間復雜度為O(E logV)。
總結
以上是生活随笔為你收集整理的图论(九)最小生成树-Kruskal算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图论(八)最小生成树
- 下一篇: 图论(十)最小生成树-Prim算法