Python小白的数学建模课-图论的基本概念
8 [/ o. o! V4 f+ r' q$ g' T& E, M- ~4 {
; }& c0 ]) L3 H$ G: ^0 u5 O- 图论中所说的图,不是图形图像或地图,而是指由顶点和边所构成的图形结构。
- 图论不仅与拓扑学、计算机数据结构和算法密切相关,而且正在成为机器学习的关键技术。
- 本系列结合数学建模的应用需求,来介绍 NetworkX 图论与复杂网络工具包的基本功能和典型算法。
6 H( ?; r# i6 w- p# X # F1 O5 p3 _& b, l, w
1. 图论1.1 图论是什么# B- B U; t5 |9 C9 z
图论〔Graph Theory〕以图为研究对象,是离散数学的重要内容。图论不仅与拓扑学、计算机数据结构和算法密切相关,而且正在成为机器学习的关键技术。5 {+ F: p7 C9 [' I# X8 Z
/ K5 q4 o* Z# G) M, F- d3 U
图论中所说的图,不是指图形图像(image)或地图(map),而是指由顶点(vertex)和连接顶点的边(edge)所构成的关系结构。
" v! D+ |: K& c! c/ _! A; z( \; m5 l8 ~2 E: U( Y- }( Z8 L
图提供了一种处理关系和交互等抽象概念的更好的方法,它还提供了直观的视觉方式来思考这些概念。
# ~6 b {8 o. {+ p5 e. l* L; V9 I) ^) {
1.2 NetworkX 工具包
4 U; J1 {; P- l2 u& mNetworkX 是基于 Python 语言的图论与复杂网络工具包,用于创建、操作和研究复杂网络的结构、动力学和功能。
$ P8 J1 v2 W7 @% z3 v7 N" Q! Y' s! n& b" n+ R5 I8 H* E- W
NetworkX 可以以标准和非标准的数据格式描述图与网络,生成图与网络,分析网络结构,构建网络模型,设计网络算法,绘制网络图形。
: A: X' |: T4 A+ N2 L0 N: T( R& C! f: ?# B1 |( M; ~+ ^! y8 {
NetworkX 提供了图形的类、对象、图形生成器、网络生成器、绘图工具,内置了常用的图论和网络分析算法,可以进行图和网络的建模、分析和仿真。1 h, ]- H/ [* }# o7 x
9 Y8 ~, t( X& O- s NNetworkX 的功能非常强大和庞杂,所涉及内容远远、远远地超出了数学建模的范围,甚至于很难进行系统的概括。本系列结合数学建模的应用需求,来介绍 NetworkX 图论与复杂网络工具包的基本功能和典型算法。
4 w9 L; s/ e) U' c
' Z# X. w, i5 s" o![]()
4 U+ C+ b( j( H; V2 m2、图、顶点和边的创建与基本操作
# L- P$ E7 ]* E图由顶点和连接顶点的边构成,但与顶点的位置、边的曲直长短无关。 Networkx 支持创建简单无向图、有向图和多重图;内置许多标准的图论算法,节点可为任意数据;支持任意的边值维度,功能丰富,简单易用。 2.1 图的基本概念
! p6 e5 K, c, V& K/ {图(Graph):图是由若干顶点和连接顶点的边所构成关系结构。3 `# N( P! I' ?0 _
顶点(Node):图中的点称为顶点,也称节点。6 [8 o2 V/ ^* a2 C# S- B: N" x
边(Edge):顶点之间的连线,称为边。
) j6 X6 I3 w- l8 Z# Z7 q平行边(Parallel edge):起点相同、终点也相同的两条边称为平行边。
6 {, i G6 x' c$ O( @ [0 z循环(Cycle):起点和终点重合的边称为循环。
* i0 z6 N& b$ A" m8 y有向图(Digraph):图中的每条边都带有方向,称为有向图。: |6 `) `3 p- N
无向图(Undirected graph):图中的每条边都没有方向,称为无向图。
- ~( t1 @) n2 X) b c$ c, U# H赋权图(Weighted graph):图中的每条边都有一个或多个对应的参数,称为赋权图。该参数称为这条边的权,权可以用来表示两点间的距离、时间、费用。
+ \4 P% c$ f( w7 M- ]度(Degree):与顶点相连的边的数量,称为该顶点的度。9 M% W4 a7 F$ S. t$ a/ V
1 P! s/ }$ u- w2 r0 p1 R2.2 图、顶点和边的操作
0 U" `5 x0 y( d7 i; PNetworkx很容易创建图、向图中添加顶点和边、从图中删除顶点和边,也可以查看、删除顶点和边的属性。3 x" B: v. z$ a& R" u
4 L5 y' i% E: O% c. D0 X2.2.1 图的创建Graph() 类、DiGraph() 类、MultiGraph() 类和 MultiDiGraph() 类分别用来创建:无向图、有向图、多图和有向多图。定义和例程如下: `' H% N# E2 n8 W7 M1 S/ Y! ^
& p. @; n R* i7 o6 @7 O. `class Graph(incoming_graph_data=None, **attr)
' Y. V3 t1 a3 `3 r; L; ^" x/ ]& _7 ~import networkx as nx # 导入 NetworkX 工具包& W/ y$ e/ {; T) M
- X- _5 d9 Z7 U( E# 创建 图& s4 c5 Z$ _+ ^7 ?. a' c
G1 = nx.Graph() # 创建:空的 无向图9 `- U* H* o* H( ]4 j
G2 = nx.DiGraph() #创建:空的 有向图
. s" E! B& S4 _4 _. o: I' {G3 = nx.MultiGraph() #创建:空的 多图
: s/ N% H# j" W! r5 E* zG4 = nx.MultiDiGraph() #创建:空的 有向多图
6 }8 ]+ I, f" r; C: {
6 d/ v6 L, [( f. u" [$ \* {6 m( t1 T' | F: M6 Q$ Z
2.2.2 顶点的添加、删除和查看
$ J. j5 v" E4 `7 H; l3 }6 P* t/ I& @" [图的每个顶点都有唯一的标签属性(label),可以用整数或字符类型表示,顶点还可以自定义任意属性。 顶点的常用操作:添加顶点,删除顶点,定义顶点属性,查看顶点和顶点属性。定义和例程如下:
5 J: B/ v1 E+ A7 o& ^Graph.add_node(node_for_adding, **attr); b! U$ v( B' j% `
Graph.add_nodes_from(nodes_for_adding, **attr)
0 [1 p; t8 S) X- F8 ~' w1 W' sGraph.remove_node(n), p( X$ R8 ]/ r' p
Graph.remove_nodes_from(nodes)
- ~! G' Q+ h1 B4 R. r4 \' J0 a3 g6 z- B1 Q) {, [6 O2 d6 F& e
# 顶点(node)的操作
# s" ?1 v4 ^. D/ Q; p, a( d. Z# 向图中添加顶点
; t# Z1 G8 w9 W. J7 R, tG1.add_node(1) # 向 G1 添加顶点 1
) X5 \$ F9 x+ e7 uG1.add_node(1, name='n1', weight=1.0) # 添加顶点 1,定义 name, weight 属性
; P' W6 i1 O# C* O' m" z9 r0 jG1.add_node(2, date='May-16') # 添加顶点 2,定义 time 属性: i7 q( g/ h( {2 m2 G* d
G1.add_nodes_from([3, 0, 6], dist=1) # 添加多个顶点,并定义属性* R J* a( d8 d
G1.add_nodes_from(range(10, 15)) # 向图 G1 添加顶点 10~140 {% ^: C4 V7 J) O
3 i& q' ^- y0 F5 p, @8 Q2 k
# 查看顶点和顶点属性, X4 E% t. r. v$ i4 k3 U
print(G1.nodes()) # 查看顶点列表
: G2 H+ C8 g& u/ e0 l* y2 R3 A6 [# [1, 2, 3, 0, 6, 10, 11, 12, 13, 14]/ \7 D4 G5 U+ `6 Q8 ^
print(G1._node) # 查看顶点属性# o5 v) a; \0 b0 w9 v* e7 \
# {1: {'name': 'n1', 'weight': 1.0}, 2: {'date': 'May-16'}, 3: {'dist': 1}, 0: {'dist': 1}, 6: {'dist': 1}, 10: {}, 11: {}, 12: {}, 13: {}, 14: {}}
4 @+ y4 W" O5 `8 \
6 E* S' W3 d, X# 从图中删除顶点
8 d: H2 H! M% X: Y2 ?; l1 tG1.remove_node(1) # 删除顶点
- _* ^5 p& Q! L3 k, I* [+ ?6 U9 mG1.remove_nodes_from([1, 11, 13, 14]) # 通过顶点标签的 list 删除多个顶点: T+ s0 J5 P; R4 B; e
print(G1.nodes()) # 查看顶点: q. b7 r$ z: F! e& m
# [2, 3, 0, 6, 10, 12] # 顶点列表# o! _$ X& r% Y; B. f, f+ A
2.2.3 边的添加、删除和查看边是两个顶点之间的连接,在 NetworkX 中 边是由对应顶点的名字的元组组成 e=(node1,node2)。边可以设置权重、关系等属性。 边的常用操作:添加边,删除边,定义边的属性,查看边和边的属性。向图中添加边时,如果边的顶点是图中不存在的,则自动向图中添加该顶点。 Graph.add_edge(u_of_edge, v_of_edge, **attr)
+ n* c; n7 V7 a) cGraph.add_edges_from(ebunch_to_add, **attr)
( d% d. v( `1 V( Q" }' OGraph.add_weighted_edges_from(ebunch_to_add, weight=‘weight’, **attr)
" ~+ ^, H. E, w2 \2 X, d
4 j" j" S+ [0 T9 [/ h' q# 边(edge)的操作
/ E( H3 _0 ]4 {; s# 向图中添加边; ?- l: l: `8 A7 l4 I
G1.add_edge(1,5) # 向 G1 添加边,并自动添加图中没有的顶点
3 ~ t* ?5 Z& J' s# SG1.add_edge(0,10, weight=2.7) # 向 G1 添加边,并设置边的属性
! S; H7 V- [" K+ P! dG1.add_edges_from([(1,2,{'weight':0}), (2,3,{'color':'blue'})]) # 向图中添加边,并设置属性4 @' U7 o" S. L
G1.add_edges_from([(3,6),(1,2),(6,7),(5,10),(0,1)]) # 向图中添加多条边, u4 _/ U w3 P6 A0 A3 Q5 t
G1.add_weighted_edges_from([(1,2,3.6),[6,12,0.5]]) # 向图中添加多条赋权边: (node1,node2,weight)
) Q! ^1 R8 T0 U2 Z, Nprint(G1.nodes()) # 查看顶点 g6 m: D* P1 I3 D& z1 S% J; k A
# [2, 3, 0, 6, 10, 12, 1, 5, 7] # 自动添加了图中没有的顶点, ~% v' }8 i/ c ?6 f8 S5 k1 Y- F6 a
K, R! i5 Y7 e% w# 从图中删除边
* V' J8 A3 X+ m+ S8 o" _G1.remove_edge(0,1) # 从图中删除边 0-1
6 E) o* W! s6 pG1.remove_edges_from([(2,3),(1,5),(6,7)]) # 从图中删除多条边
! f9 b1 }: y& G6 y# ?, T0 v3 j1 p. u! [1 p
# 查看 边和边的属性1 e+ h c& I/ N4 ^
print(G1.edges) # 查看所有的边9 e7 s1 \3 @1 B3 y z y f* y% j
[(2, 1), (3, 6), (0, 10), (6, 12), (10, 5)]
) @, l) y' C$ Y: I, N+ _print(G1.get_edge_data(1,2)) # 查看指定边的属性" G/ A# v5 V+ {5 v# r7 s
# {'weight': 3.6}
. x, ?" p& T: L) q2 sprint(G1[1][2]) # 查看指定边的属性4 b" p) J9 ^8 N' R ?+ ~: [& [0 Y$ F
# {'weight': 3.6}
: Z/ w, Y9 Y; mprint(G1.edges(data=True)) # 查看所有边的属性: W+ E7 w: c- Q: m' I3 ~2 M" T; L. C
# [(2, 1, {'weight': 3.6}), (3, 6, {}), (0, 10, {'weight': 2.7}), (6, 12, {'weight': 0.5}), (10, 5, {})]: `2 ?6 P$ ?+ C1 T
. u( ~" Q0 [8 v% _
2.2.4 查看图、顶点和边的信息/ B- E0 ] V2 v6 J0 Q( D
b2 [: K" d4 w% X1 a$ `# 查看图、顶点和边的信息+ k2 l" N% @/ |+ z
print(G1.nodes) # 返回所有的顶点 [node1,...]( U6 N% ^+ m! `3 J# h# B1 n
# [2, 3, 0, 6, 10, 12, 1, 5, 7]! n9 X7 S3 r7 k& _3 u
print(G1.edges) # 返回所有的边 [(node1,node2),...]; m! r$ \& S3 a
# [(2, 1), (3, 6), (0, 10), (6, 12), (10, 5)]. w* X; Z5 Z3 j. u% p0 `
print(G1.degree) # 返回各顶点的度 [(node1,degree1),...]
1 U+ S7 x& r' @* b# [(2, 1), (3, 1), (0, 1), (6, 2), (10, 2), (12, 1), (1, 1), (5, 1), (7, 0)]# q% G# n: V1 R' Y" U
print(G1.number_of_nodes()) # 返回顶点的数量+ O4 s; c7 r8 F1 O9 J& N
# 9* P2 w0 I! m+ W, F; |1 B
print(G1.number_of_edges()) # 返回边的数量' z" T- `# `6 R* L6 J
# 5
9 W* L! o. P5 l3 ?$ T- A2 bprint(G1[10]) # 返回与指定顶点相邻的所有顶点的属性
) q1 h0 y6 q/ e) t: w# {0: {'weight': 2.7}, 5: {}}
' V7 w) o% Y4 yprint(G1.adj[10]) # 返回与指定顶点相邻的所有顶点的属性
' {1 ~. {0 |& `# {0: {'weight': 2.7}, 5: {}}% y5 i A# I: H1 r
print(G1[1][2]) # 返回指定边的属性( T: U. j7 x0 D1 T6 H _( A
# {'weight': 3.6}% W8 L! g8 x5 ]/ _! {
print(G1.adj[1][2]) # 返回指定边的属性
6 F( k t. c4 k# {'weight': 3.6}
) X0 A$ `/ D5 l9 P4 Pprint(G1.degree(10)) # 返回指定顶点的度2 \( F* k7 x2 L: E1 @+ v
# 2
1 e1 v6 Z) Z4 D# l1 G+ }5 y. A# W6 o, O
print('nx.info:',nx.info(G1)) # 返回图的基本信息
) T: [% q# b% o Z) I6 k4 d5 d# \print('nx.degree:',nx.degree(G1)) # 返回图中各顶点的度
3 ?2 J) N* E! B5 r& k, m* rprint('nx.density:',nx.degree_histogram(G1)) # 返回图中度的分布$ I/ w0 t7 f) h! J* n9 @
print('nx.pagerank:',nx.pagerank(G1)) # 返回图中各顶点的频率分布
& R+ G& I8 n6 A; h& ` }" o p5 n2 @7 L) y& C
8 H, ^; A8 ?0 f9 Y$ |5 R" o8 ~. V: J+ T
1 e/ s) }5 P9 P* w. H0 ]
5 I8 Q1 a* C$ O2 W2.3 图的属性和方法图的方法% k' n: A+ A0 |
5 t1 I% k2 A9 S. f4 |
方法 说明- H( S+ V6 Q+ Q- l1 I9 {8 p
G.has_node(n) 当图 G 中包括顶点 n 时返回 True
% d5 U* s7 a u5 o7 CG.has_edge(u, v) 当图 G 中包括边 (u,v) 时返回 True
1 E# U! }" M' bG.number_of_nodes() 返回 图 G 中的顶点的数量
' Q( P6 u' y- K3 ] }G.number_of_edges() 返回 图 G 中的边的数量
; u) n# N7 a5 ^ h4 c7 Z9 h6 c yG.number_of_selfloops() 返回 图 G 中的自循环边的数量
# R% I8 O; A+ cG.degree([nbunch, weight]) 返回 图 G 中的全部顶点或指定顶点的度! S/ k* p1 I0 q
G.selfloop_edges([data, default]) 返回 图 G 中的全部的自循环边/ \1 N; S* Z3 z9 e8 l, S6 v
G.subgraph([nodes]) 从图 G1中抽取顶点[nodes]及对应边构成的子图/ j k& N' q$ w6 k% ?# N
union(G1,G2) 合并图 G1、G2
6 t& E' F# t7 k- {+ pnx.info(G) 返回图的基本信息. T. e0 [! m3 {% @
nx.degree(G) 返回图中各顶点的度! E: ~- m$ _' A: q
nx.degree_histogram(G) 返回图中度的分布
& @2 m! [- V: y; knx.pagerank(G) 返回图中各顶点的频率分布
- s1 J$ |1 T$ v7 F3 ]nx.add_star(G,[nodes],**attr) 向图 G 添加星形网络( H* W p6 N: t
nx.add_path(G,[nodes],**attr) 向图 G 添加一条路径6 k/ y+ W' s$ D5 V( m; K5 f
nx.add_cycle(G,[nodes],**attr) 向图 G 添加闭合路径
% z- d/ |; b4 `0 |3 K
/ i9 E/ X. M2 x4 o' l3 \3 R! f0 s% g* G" V2 ]$ H6 ^
例程:
& C# N' `/ M+ s7 ]- bG1.clear() # 清空图G18 {; ?* u4 |, g
nx.add_star(G1, [1, 2, 3, 4, 5], weight=1) # 添加星形网络:以第一个顶点为中心
1 q5 }; r& m) d, Z9 _- H# [(1, 2), (1, 3), (1, 4), (1, 5)]
X4 _# z. D9 o4 `- E$ gnx.add_path(G1, [5, 6, 8, 9, 10], weight=2) # 添加路径:顺序连接 n个节点的 n-1条边
( k9 v/ K' T8 c: {* t, {* i# [(5, 6), (6, 8), (8, 9), (9, 10)]
9 @( a# H7 ]- i) xnx.add_cycle(G1, [7, 8, 9, 10, 12], weight=3) # 添加闭合回路:循环连接 n个节点的 n 条边
; F4 m* q8 n' b/ ?# [(7, 8), (7, 12), (8, 9), (9, 10), (10, 12)]" Y- a$ M; M# Q9 |
print(G1.nodes) # 返回所有的顶点 [node1,...]
; M0 R/ K6 X6 dnx.draw_networkx(G1)3 I1 U% q& B- I1 P5 u5 n+ a" m
plt.show()5 }/ e( N f3 {- E! L# k
; h2 P. w, k$ m5 m0 i. _# V
G2 = G1.subgraph([1, 2, 3, 8, 9, 10])
5 p4 A5 K% \- EG3 = G1.subgraph([4, 5, 6, 7])5 ?: t N' q0 B4 C+ T" n
G = nx.union(G2, G3). O2 d4 p, v ^
print(G.nodes) # 返回所有的顶点 [node1,...]
7 K! w D+ |4 j- ?# E, m- Q8 y# [1, 2, 3, 8, 9, 10, 4, 5, 6, 7]
3 |& U- E/ K8 L/ a3 [5 v# s' A& I6 f5 }, B2 s' p/ S7 Z( Z [: Z* ]
+ r: J0 Q0 p5 E" ?& b! r
3、图的绘制与分析3.1 图的绘制$ ]) w a4 |+ L4 H# d& I2 c+ g
可视化是图论和网络问题中很重要的内容。NetworkX 在 Matplotlib、Graphviz 等图形工具包的基础上,提供了丰富的绘图功能。
9 T! c: h; F! H- B* O
2 z+ y, ~; x) E. q, i# M本系列拟对图和网络的可视化作一个专题,在此只简单介绍基于 Matplotlib 的基本绘图函数。基本绘图函数使用字典提供的位置将节点放置在散点图上,或者使用布局函数计算位置。* j* H" n* g5 r
/ l3 T+ N# A/ ?& E4 C c
方法 说明- d" b$ _/ Z7 a* \
draw(G[,pos,ax]) 基于 Matplotlib 绘制 图 G9 Y% O( G( R3 r- I2 i1 H# ?
draw_networkx(G[, pos, arrows, with_labels]) 基于 Matplotlib 绘制 图 G
* l4 t, T: @, v& edraw_networkx_nodes(G, pos[, nodelist, . . . ]) 绘制图 G 的顶点
" G8 s, F( u7 n; n1 ldraw_networkx_edges(G, pos[, edgelist, . . . ]) 绘制图 G 的边
8 r( h3 Z$ `, |7 b' H" [draw_networkx_labels(G, pos[, labels, . . . ]) 绘制顶点的标签8 i7 r. p; P; O) q
draw_networkx_edge_labels(G, pos[, . . . ]) 绘制边的标签) e4 o+ e% t) O6 R
# v# w+ n: K6 R, v, p5 p
8 H# x* q ~- a. f7 U& U其中,nx.draw() 和 nx.draw_networkx() 是最基本的绘图函数,并可以通过自定义函数属性或其它绘图函数设置不同的绘图要求。) {; x! l8 v' }/ C
draw(G, pos=None, ax=None, **kwds) draw_networkx(G, pos=None, arrows=True, with_labels=True, **kwds) 4 @9 I; ?; N& X6 V! z: q
常用的属性定义如下:/ r% U" x) s+ r, I) q5 P
" G; T& h6 I" N6 {‘node_size’:指定节点的尺寸大小,默认300' C0 s3 O: R y! y# z3 w
‘node_color’:指定节点的颜色,默认红色
: i; U& @3 s+ P+ D. J‘node_shape’:节点的形状,默认圆形) i# `- F" N% b3 h. m1 Q1 @- U
'‘alpha’:透明度,默认1.0,不透明3 C1 @6 [1 D% V1 q4 v
‘width’:边的宽度,默认1.0
% i9 Z: @8 j+ z; A2 D! L4 n‘edge_color’:边的颜色,默认黑色* S( g+ u8 k! {7 }: O
‘style’:边的样式,可选 ‘solid’、‘dashed’、‘dotted’、‘dashdot’
5 E7 w3 s k5 r& v [+ |0 r‘with_labels’:节点是否带标签,默认True7 W4 F7 t. D) `# [& } ^7 E- S! x
‘font_size’:节点标签字体大小,默认12+ J" j! u% R8 \: d5 ?. M
‘font_color’:节点标签字体颜色,默认黑色
" v. {2 \$ S% k( ^5 v" a$ M+ T# t8 z9 g' r: D
, h, d$ x7 C' n% o! a
3.2 图的分析NetwotkX 提供了图论函数对图的结构进行分析# H/ m( M: J- _
子图' m# z: G/ W/ l0 J7 @
- 子图是指顶点和边都分别是图 G 的顶点的子集和边的子集的图。
- subgraph()方法,按顶点从图 G 中抽出子图。例程如前。1 }$ n( \. ] ]: m* q) h
连通子图
/ Z! |. ]( R2 o* v4 n1 v0 K
6 r" }9 ^# j# r7 w- 如果图 G 中的任意两点间相互连通,则 G 是连通图。
- [color=rgba(0, 0, 0, 0.749019607843137)]connected_components()方法,返回连通子图的集合。' h$ o/ Q# U- J, U9 [8 u/ c
[color=rgba(0, 0, 0, 0.749019607843137)]G = nx.path_graph(4)[color=rgba(0, 0, 0, 0.749019607843137)]nx.add_path(G, [7, 8, 9])[color=rgba(0, 0, 0, 0.749019607843137)]# 连通子图[color=rgba(0, 0, 0, 0.749019607843137)]listCC = [len(c) for c in sorted(nx.connected_components(G), key=len, reverse=True)][color=rgba(0, 0, 0, 0.749019607843137)]maxCC = max(nx.connected_components(G), key=len)[color=rgba(0, 0, 0, 0.749019607843137)]print('Connected components:{}'.format(listCC)) # 所有连通子图[color=rgba(0, 0, 0, 0.749019607843137)]# Connected components:[4, 3][color=rgba(0, 0, 0, 0.749019607843137)]print('Largest connected components:{}'.format(maxCC)) # 最大连通子图[color=rgba(0, 0, 0, 0.749019607843137)]# Largest connected components:{0, 1, 2, 3}[color=rgba(0, 0, 0, 0.749019607843137)]强连通如果有向图 G 中的任意两点间相互连通,则称 G 是强连通图。strongly_connected_components()方法,返回所有强连通子图的列表。# 强连通G = nx.path_graph(4, create_using=nx.DiGraph())nx.add_path(G, [3, 8, 1])# 找出所有的强连通子图con = nx.strongly_connected_components(G)print(type(con),list(con))# <class 'generator'> [{8, 1, 2, 3}, {0}]弱连通如果一个有向图 G 的基图是连通图,则有向图 G 是弱连通图。weakly_connected_components()方法,返回所有弱连通子图的列表。# 弱连通G = nx.path_graph(4, create_using=nx.DiGraph()) #默认生成节点 0,1,2,3 和有向边 0->1,1->2,2->3nx.add_path(G, [7, 8, 3]) #生成有向边:7->8->3con = nx.weakly_connected_components(G)print(type(con),list(con))# <class 'generator'> [{0, 1, 2, 3, 7, 8}]
, D$ l8 F3 i; h, |) T. G# ?
1 B4 q- p- n6 ~, b* r" q5 k8 G! c- J, v3 J' y$ Y
6 `6 }, x# Y8 R) {2 n# T+ C4 b( }. ~+ t! D8 O2 T# r
|