1. 图的基本概念
图(graph)是一种非线性数据结构,由顶点(vertex)和边(edge)组成。我们可以将图 $G$ 抽象地表示为一组顶点 $V$ 和一组边 $E$ 的集合。以下示例展示了一个包含 5 个顶点和 7 条边的图。
\[\begin{aligned} V & = \{ 1, 2, 3, 4, 5 \} \\ E & = \{ (1,2), (1,3), (1,5), (2,3), (2,4), (2,5), (4,5) \} \\ G & = \{ V, E \} \\ \end{aligned}\]如果我们将顶点看作节点,将边看作连接节点的引用(或指针),则图可以视为从链表扩展而来的数据结构。如下图所示,相较于线性结构(链表)和分支结构(树),网络结构(图)的自由度更高,因此更为复杂。
2. 图的常见类型与术语
根据边的方向性,图可分为无向图(Undirected Graph)和有向图(Directed Graph),如下图所示。
- 无向图:边表示两个顶点之间的双向连接关系。例如,社交应用中的“好友关系”。
- 有向图:边具有方向性,即 $A \rightarrow B$ 与 $A \leftarrow B$ 是独立的。例如,社交媒体中的“关注”与“被关注”关系。
根据所有顶点是否连通,图可分为连通图(Connected Graph)和非连通图(Disconnected Graph),如下图所示。
- 连通图:从任一顶点出发,可以到达其余任意顶点。
- 非连通图:至少存在一个顶点无法从其他顶点到达。
图的边还可以赋予权重,从而得到有权图(Weighted Graph)。例如,在多人在线游戏中,系统根据共同游戏时间计算玩家之间的“亲密度”,这种亲密度网络就可以用有权图来表示。
图数据结构中常见的术语包括:
- 邻接(Adjacency):当两顶点之间存在边相连时,称这两顶点为邻接顶点。在上图中,顶点 1 的邻接顶点为顶点 2、3、5。
- 路径(Path):从顶点 A 到顶点 B 经过的边构成的序列称为从 A 到 B 的路径。在上图中,边序列 1-5-2-4 是从顶点 1 到顶点 4 的一条路径。
- 度(Degree):一个顶点拥有的边数。在有向图中,入度(In-degree)表示指向该顶点的边数,出度(Out-degree)表示从该顶点指出的边数。
3. 图的表示方法
图常用的表示方法有“邻接矩阵”和“邻接表”。以下以无向图为例进行说明。
3.1. 邻接矩阵
设图的顶点数为 $n$,邻接矩阵(Adjacency Matrix)用一个 $n \times n$ 的矩阵表示图。矩阵的行和列代表顶点,矩阵的元素表示顶点之间的边,用 $1$ 表示有边相连,用 $0$ 表示无边相连。
如下图所示,设邻接矩阵为 $M$,顶点列表为 $V$,矩阵元素 $M[i, j] = 1$ 表示顶点 $V[i]$ 和顶点 $V[j]$ 之间存在边,反之则表示两顶点之间无边。
邻接矩阵具有以下特性:
- 顶点不能与自身相连,因此邻接矩阵的主对角线元素没有意义。
- 对于无向图,矩阵关于主对角线对称。
- 将邻接矩阵的 $1$ 和 $0$ 替换为权重,可以表示有权图。
使用邻接矩阵表示图时,可以直接访问矩阵元素获取边信息,增删查改操作效率高,时间复杂度为 $O(1)$。但由于矩阵的空间复杂度为 $O(n^2)$,内存占用较多。
3.2. 邻接表
邻接表(Adjacency List)用 $n$ 个链表表示图,链表的节点表示顶点。第 $i$ 个链表对应顶点 $i$,其中存储了该顶点的所有邻接顶点(与该顶点相连的顶点)。下图展示了一个使用邻接表存储的图的示例。
邻接表只存储实际存在的边,因此空间利用更为高效,尤其当边的数量远小于 $n^2$ 时。然而,在邻接表中查找边时需要遍历链表,时间效率不如邻接矩阵。
观察上图,邻接表的结构与哈希表中的“链式地址”非常相似,可以使用类似的方法优化效率。例如,当链表较长时,可以将链表转化为 AVL 树或红黑树,将查找效率优化至 $O(\log n)$;或者将链表转换为哈希表,将时间复杂度降至 $O(1)$。
4. 图的常见应用
下表展示了图在现实生活中的一些常见应用场景,相应的问题通常可以通过图计算方法来解决。
表 1: 现实生活中常见的图应用
场景 | 顶点 | 边 | 图计算问题 |
---|---|---|---|
社交网络 | 用户 | 好友关系 | 潜在好友推荐 |
地铁线路 | 站点 | 站点间的连通性 | 最短路线推荐 |
太阳系 | 星体 | 星体间的万有引力作用 | 行星轨道计算 |