一、图的概念

图形由顶点/节点和连接这些顶点的边/线组成。
图可以是无向的(意味着在与每个双向边相关联的两个顶点之间没有区别)或者可以指向图(意味着其边缘从一个顶点指向另一个顶点但不一定在另一个方向上)。
可以对图形进行加权(通过向每个边缘分配权重,其表示与该连接相关联的数值)或者图形可以是未加权的(所有边缘具有单位权重1或者所有边缘具有相同的恒定权重)

如何表现图的权重矩阵_权重

  • 顶点(vertex)

顶点为上图中带数字的圆形,表示某个事物或对象。由于图的术语没有标准化,因此,称顶点为点、节点、结点、端点等都是可以的。

  • 边(edge)

边为上图中顶点之间的线条,表示事物与事物之间的关系,需要注意的是边表示的是顶点之间的逻辑关系。

  • 有向/无向图(Directed Graph/ Undirected Graph)

最基本的图通常被定义为无向图(上两幅图),与之对应的则被称为有向图(下两幅图)。两者唯一的区别在于,有向图中的边是有方向性的。

  • 权重(weight)

边的权重(或者称为权值、开销、长度等),为每条边都有与之对应的值,右侧两幅图为有权图。

  • 连通图/连通分量(connected graph/connected component)

如果在图中,任意2个顶点之间都存在路径(不需要直接连接),那么称其为连通图,上面的图都是连通图。

二、图的表示方法

如何表现图的权重矩阵_邻接矩阵_02

2.1 邻接矩阵

如何表现图的权重矩阵_连通图_03


邻接矩阵的核心思想是针对每个顶点设置一个表,这个表包含所有顶点,通过True/False来表示是否是邻居顶点,[0,1]为True即0->1。

N = [[0, 1, 1, 0, 0],  # 0 的邻接情况
     [0, 0, 1, 1, 0],  # 1 的邻居表
     [0, 0, 0, 1, 0],  # 2 的邻居表
     [0, 0, 0, 0, 1],  # 3 的邻居表
     [0, 0, 0, 0, 0]]  # 4 的邻居表

邻接矩阵操作

# 顶点1是否是0的邻居顶点
print(N[1][0])

# 顶点1的邻居顶点个数
print(sum(N[1]))

# 顶点1的邻居顶点
neighbour = []
for i in range(len(N[1])):
     if N[1][i]:
          neighbour.append(i)
print(neighbour)

2.2 邻接列表

如何表现图的权重矩阵_连通图_04

邻接列表的核心思想就是针对每个顶点设置一个邻居表。

N = [{1, 2},  # 0 的邻居表
     {2, 3},  # 1 的邻居表
     {3},  # 2 的邻居表
     {4},  # 3 的邻居表
     {}]  # 4 的邻居表

邻接列表操作

# 顶点1的邻居顶点
print(N[1])
# 顶点1是否是0的邻居顶点
print(1 in N[0])
# 顶点1的邻居顶点个数
print(len(N[1]))

因为不能重复存储邻居顶点,所以每个顶点的邻居表都是一个集合(set)。当表示带权重值的图时,可以使用字典。

N = [{1: 1, 2: 1},  # 0 的邻居表
     {2: 1, 3: 2},  # 1 的邻居表
     {3: 1},  # 2 的邻居表
     {4: 1},  # 3 的邻居表
     {}]  # 4 的邻居表

# 边(0,1)的权重
if 1 in N[0]:
     print(N[0][1])

2.3 边缘列表

如何表现图的权重矩阵_邻接矩阵_05

N = [{0, 0},  # 0 的邻居表
     {0, 2},  
     {1, 2},  # 1 的邻居表
     {1, 3},  
     {2, 3},  # 2 的邻居表
     {3, 4}]  # 3 的邻居表