一、图的概念
图形由顶点/节点和连接这些顶点的边/线组成。
图可以是无向的(意味着在与每个双向边相关联的两个顶点之间没有区别)或者可以指向图(意味着其边缘从一个顶点指向另一个顶点但不一定在另一个方向上)。
可以对图形进行加权(通过向每个边缘分配权重,其表示与该连接相关联的数值)或者图形可以是未加权的(所有边缘具有单位权重1或者所有边缘具有相同的恒定权重)
- 顶点(vertex)
顶点为上图中带数字的圆形,表示某个事物或对象。由于图的术语没有标准化,因此,称顶点为点、节点、结点、端点等都是可以的。
- 边(edge)
边为上图中顶点之间的线条,表示事物与事物之间的关系,需要注意的是边表示的是顶点之间的逻辑关系。
- 有向/无向图(Directed Graph/ Undirected Graph)
最基本的图通常被定义为无向图(上两幅图),与之对应的则被称为有向图(下两幅图)。两者唯一的区别在于,有向图中的边是有方向性的。
- 权重(weight)
边的权重(或者称为权值、开销、长度等),为每条边都有与之对应的值,右侧两幅图为有权图。
- 连通图/连通分量(connected graph/connected component)
如果在图中,任意2个顶点之间都存在路径(不需要直接连接),那么称其为连通图,上面的图都是连通图。
二、图的表示方法
2.1 邻接矩阵
邻接矩阵的核心思想是针对每个顶点设置一个表,这个表包含所有顶点,通过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 邻接列表
邻接列表的核心思想就是针对每个顶点设置一个邻居表。
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 边缘列表
N = [{0, 0}, # 0 的邻居表
{0, 2},
{1, 2}, # 1 的邻居表
{1, 3},
{2, 3}, # 2 的邻居表
{3, 4}] # 3 的邻居表