最小生成树分析以及Python代码(networkx实现,画图)

目录

最小生成树的概念与算法

Kruskal 算法

Prim 算法

python代码(networkx)

函数定义

函数测试

画图

1. 如果画单个图可以这样画图:

 2. 高亮必须这样做:

3. 添加标签:

参考:


最小生成树的概念与算法

最小生成树(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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值