圖的基本概念:
- 圖:圖由頂點集V和邊集E組成,表示爲G=(V,E);
- 邊權:邊e具有權重,(結合不同環境具體可以理解爲兩點的距離、相似度)
- 樹:任意兩點都有路徑相連,但是沒有迴路
- 最小生成樹(MST:minimum spanning tree):邊權之和最小的樹,n個頂點產生(n-1)個邊
Kruskal算法原理:
- 將邊按權重從小到大進行排序;
- 將每個頂點獨立視爲根節點,產生n個樹;
- 依次選取每條邊,如果邊的兩個頂點不屬於同一個樹,則將其合併,如果屬於同一個樹(意味着會形成迴路),則將其捨棄,考慮下一條邊,最後形成(n-1)條邊
Kruskal算法解析
圖1由頂點集{‘A’,‘B’,‘C’,‘D’,‘E’,‘F’,‘G’}和一系列帶有權重的邊組成(本質上n個頂點兩兩相連可以形成n(n-1)/2條邊,圖中省略部分邊及邊權)
將7個頂點視爲7個樹的根節點。首先選取權重最小的邊e(BF),其頂點爲B、F,兩點不屬於同一棵樹,故合併。
然後選取權重爲3的邊e(CD),兩點也不屬於同一棵樹,故合併。
重複上述步驟,依此得到:
當選取到權重爲6的邊e(EF)時,發現頂點E,F同屬於一棵樹,故舍棄,最後最小生成樹有兩種情況,如圖7,圖8
實操
1.定義頂點
vertices=list('ABCDEFG')
2.定義邊並按邊權進行排序
edges = [("A", "B", 5), ("A", "G", 7),
("B", "F", 1), ("C", "F", 4),
("C", "D", 3), ("C", "E", 7),
("E", "F", 6), ("D", "E", 4),
("E", "G", 12),("F", "G", 12)]
edges.sort(key=lambda x:x[2])
print(edges)
輸出如下:
3.將每個頂點視爲一棵節點樹,可以用字典表示,鍵表示頂點,鍵值表示頂點所在樹的節點
ori_trees=dict()
for i in vertices:
ori_trees[i]=i
print(ori_trees)
輸出爲:
4.根據邊的兩個頂點的根節點是否相同考慮是否合併
#尋找根節點
def find_node(x):
if ori_trees[x]!=x:
ori_trees[x]=find_node(ori_trees[x])
return ori_trees[x]
#定義最小生成樹
mst=[]
#定義循環次數,n爲需要添加的邊數=頂點數-1
n=len(vertices)-1
#循環
for edge in edges:
v1,v2,_=edge
if find_node(v1)!=find_node(v2):
ori_trees[find_node(v2)]=find_node(v1)
mst.append(edge)
print('添加第'+str(7-n)+'條邊後:')
n-=1
print(ori_trees)
print(mst)
if n==0:
break
輸出結果如下:
完整代碼如下:
edges = [("A", "B", 5), ("A", "G", 7),
("B", "F", 1), ("C", "F", 4),
("C", "D", 3), ("C", "E", 7),
("E", "F", 6), ("D", "E", 4),
("E", "G", 12),("F", "G", 12)]
vertices=list('ABCDEFG')
edges.sort(key=lambda x:x[2])
ori_trees=dict()
for i in vertices:
ori_trees[i]=i
#尋找根節點
def find_node(x):
if ori_trees[x]!=x:
ori_trees[x]=find_node(ori_trees[x])
return ori_trees[x]
#定義最小生成樹
mst=[]
#定義循環次數,n爲需要添加的邊數=頂點數-1
n=len(vertices)-1
#循環
for edge in edges:
v1,v2,_=edge
if find_node(v1)!=find_node(v2):
ori_trees[find_node(v2)]=find_node(v1)
mst.append(edge)
print('添加第'+str(7-n)+'條邊後:')
n-=1
print(ori_trees)
print(mst)
if n==0:
break