目录
最小生成树的概念与算法
最小生成树(Minimum Spanning Tree, MST)是一个连通图的生成树,其边的总权重最小。常用的算法有 Kruskal 算法和 Prim 算法。
Kruskal 算法
主要思想:全局最小值
算法实现:全局寻找最短的边,如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;反之,舍去。
(b)选择最小的边<v1, v3>
(c)(d)(e)依次选择了最小的边,没有问题。未产生回路。
(f)两个5中,如果选择了<v3,v4>就会产生回路v1->v3->v4->v1,因此不可以选择这一条边;然后发现第二个5刚好没有产生回路,得到完整的生成树
Prim 算法
主要思想:贪婪算法
step1:
从节点v1开始,选择距离连通集{v1}最小的节点v2,把对应的节点加入节点集{v1, v3},对应的边加入我们的联通集
step 2:
选择距离节点集最近的节点v6,加入对应的边<v3,v6>到连通集中,v6加入节点集和连通集
step 3~5:
同样的方法扩大连通集和节点集,直到所有的节点都进入节点集,于是最小生成树就产生了。
python代码(networkx)
函数定义
主要用到了的是nx.minimum_spanning_tree()和nx.tree.minimum_spanning_edges()
import matplotlib.pyplot as plt
import networkx as nx
# 创建带权边的列表
weighted_edges = [(i, j, weight) for i, j, weight in [
(1, 2, 50), (1, 3, 60), (2, 4, 65), (2, 5, 40), (3, 4, 52),
(3, 7, 45), (4, 5, 50), (4, 6, 30), (4, 7, 42), (5, 6, 70)
]]
# 创建无向图并添加边
def create_and_add_edges():
G = nx.Graph()
G.add_weighted_edges_from(weighted_edges)
return G
# 计算最小生成树
def calculate_mst(G):
mst_kruskal = nx.minimum_spanning_tree(G) # Kruskal 算法生成的最小生成树(图对象)。
mst_edges_kruskal = list(nx.tree.minimum_spanning_edges(G, algorithm="kruskal")) # Kruskal 算法生成的最小生成树的边(列表,包含边的权重),可以用来生成图的高亮边。
mst_prim = list(nx.tree.minimum_spanning_edges(G, algorithm="prim", data=False)) # Prim 算法生成的最小生成树的边(列表,not包含边的权重)。
return mst_kruskal, mst_edges_kruskal, mst_prim
函数测试
# 创建图
G = create_and_add_edges()
# 计算最小生成树
mst_kruskal, mst_edges_kruskal, mst_prim = calculate_mst(G)
# 打印节点和边
print("Nodes of MST (Kruskal):", mst_kruskal.nodes)
print("Edges of MST (Kruskal):", mst_kruskal.edges) # tree的两个属性:边和顶点
print("Sorted Edges of MST (Kruskal):", sorted(mst_kruskal.edges))
print("MST Edges (Kruskal with weights):", mst_edges_kruskal)
print("MST Edges (Prim):", mst_prim)
结果
Nodes of MST (Kruskal): [1, 2, 3, 4, 5, 7, 6]
Edges of MST (Kruskal): [(1, 2), (2, 5), (3, 7), (4, 6), (4, 7), (4, 5)]
Sorted Edges of MST (Kruskal): [(1, 2), (2, 5), (3, 7), (4, 5), (4, 6), (4, 7)]
MST Edges (Kruskal with weights): [(4, 6, {'weight': 30}), (2, 5, {'weight': 40}), (4, 7, {'weight': 42}), (3, 7, {'weight': 45}), (1, 2, {'weight': 50}), (4, 5, {'weight': 50})]
MST Edges (Prim): [(1, 2), (2, 5), (5, 4), (4, 6), (4, 7), (7, 3)]
画图
1. 如果画单个图可以这样画图:
nx.draw_networkx(G, node_color='b', font_color='w', font_weight='bold') # 先画出来原本的图
plt.show()
也就是直接用nx中的draw_networkx函数(无法高亮):
2. 高亮必须这样做:
# 设置节点
pos=nx.spring_layout(G)
nx.draw_networkx_nodes(G,pos,node_size=700)
# 绘画所有边
nx.draw_networkx_edges(G, pos)
# 绘画需要高亮的边
nx.draw_networkx_edges(G, pos, edgelist=mst_edges_kruskal, edge_color='y', width=3)
# 显示并设置标号
nx.draw_networkx_labels(G,pos,font_size=15,font_family='sans-serif', font_color='w')
# 不显示坐标轴
plt.axis('off')
plt.show()
也就是先画节点(G进行spring_layout),然后根据G的边画;然后画需要高亮的边,也就是给一个edgelist,这里是用的是有权重的list。注意都是在G的基础上画的。
保留edges中的权重可以在nx.tree.minimum_spanning_edges() 中让data=True或者缺省。
3. 添加标签:
labels = nx.get_edge_attributes(G,'weight') # 得到图中的weight(权重)
nx.draw_networkx_edge_labels(G, pos, edge_labels = labels) # 添加edge_labels
效果示意图(这里还调整了draw_networkx_nodes,添加了一个alpha=0.5的参数,好看了不少)
4. 改变结构(布局):
有多种结构是可是使用的,例如shell, circular等. 另外可以直接指定一个options,在画图的时候直接就用上了.
# from networkx.drawing.layout import circular_layout, random_layout, spectral_layout, shell_layout
from matplotlib import pyplot as plt
import networkx as nx
G=nx.Graph()
G.add_weighted_edges_from([(1,2,50),(1,3,60),(2,4,65),(2,5,40),(3,4,52),(3,7,45),(4,5,50),(4,6,30),(4,7,42),(5,6,70)])
options={
'node_color':'b',
'font_color':'w',
'font_weight':'bold',
'node_size':80,
'with_labels':True,
'width':2,
'alpha':0.8
}
subax1=plt.subplot(311)
nx.draw_networkx(G, **options)
subax1.set_title('Original Graph', fontsize=16)
subax2=plt.subplot(323)
nx.draw_circular(G, **options)
subax2.set_title('Circular Layout', fontsize=8)
subax3=plt.subplot(324)
nx.draw_random(G, **options)
subax3.set_title('Random Layout', fontsize=8)
subax4=plt.subplot(325)
nx.draw_spectral(G, **options)
subax4.set_title('Spectral Layout', fontsize=8)
subax5=plt.subplot(326)
nx.draw_shell(G, **options)
subax5.set_title('Shell Layout', fontsize=8)
plt.show()
结果:
参考:
1. 建模思路与主要代码: @若冰(马世拓)
2. 画图代码:http://t.csdnimg.cn/QF23L
3. 布局部分代码: https://zhuanlan.zhihu.com/p/536737592