一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。[1] 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。
class DisjointSet(dict):
'''不相交集'''
def __init__(self, dict):
pass
def add(self, item):
self[item] = item
def find(self, item):
if self[item] != item:
self[item] = self.find(self[item])
return self[item]
def unionset(self, item1, item2):
self[item2] = self[item1]
def Kruskal_1(nodes, edges):
'''基于不相交集实现Kruskal算法'''
forest = DisjointSet(nodes)
MST = []
for item in nodes:
print(item)
forest.add(item)
edges = sorted(edges, key=lambda element: element[2])
num_sides = len(nodes)-1 # 最小生成树的边数等于顶点数减一
for e in edges:
node1, node2, _ = e
parent1 = forest.find(node1)
parent2 = forest.find(node2)
if parent1 != parent2:
MST.append(e)
num_sides -= 1
if num_sides == 0:
return MST
else:
forest.unionset(parent1, parent2)
pass
def Kruskal(nodes, edges):
''' Kruskal 无向图生成最小生成树 '''
all_nodes = nodes # set(nodes)
used_nodes = set()
MST = []
edges = sorted(edges, key=lambda element: element[2], reverse=True)
# 对所有的边按权重升序排列
while used_nodes != all_nodes and edges:
element = edges.pop(-1)
if element[0] in used_nodes and element[1] in used_nodes:
continue
MST.append(element)
used_nodes.update(element[:2])
# print(used_nodes)
return MST
def main():
nodes = set(list('ABCDEFGHI'))
edges = [("A", "B", 4), ("A", "H", 8),
("B", "C", 8), ("B", "H", 11),
("C", "D", 7), ("C", "F", 4),
("C", "I", 2), ("D", "E", 9),
("D", "F", 14), ("E", "F", 10),
("F", "G", 2), ("G", "H", 1),
("G", "I", 6), ("H", "I", 7)]
print("\n\nThe undirected graph is :", edges)
print("\n\nThe minimum spanning tree by Kruskal is : ")
print(Kruskal_1(nodes, edges))
# print(Kruskal(nodes, edges))
if __name__ == '__main__':
main()