在cs 领域, 许多的applications都和graphs 有着密不可分的关系。 本章我们学习如何去implement一个graph, 以及如何去manipulate(处理) graphs.
Introduction
1736 年, 在Konigsberg小镇上提出了一个著名的七桥问题。
河流将陆地分割成了四个区域(A, B, C, D), 这四个land areas 被7座桥连接起来。 这七座桥标记为a, b, c, d, e, f, g。
Konigsberg bridge problem 如下:
从一个陆地区域开始, 能否walk across all the bridge once and return to the starting land area?
欧拉回答了这个问题, 伟大的数学家欧拉这个问题没有解。并创立了图论。
欧拉将该问题用graph 表达如下:
如今, 200多年过去了, 图论现在广泛应用于多个领域, 这里不再赘述。 下面我们说说graph 在计算机科学领域的应用。
图的定义
一个graph G, 是由两个集合对(pair)组成: G = (V, E), 其中V是有限非空集合, 被称为Vertices set graph(图的节点集合), E被称为the edges set f the graph(图的边集合, 也就是边集合的每一个元素均是V的pairs of elements。
我们可以用V(G)代表图G的节点集合, E(G)代表图G的边集合。
如果E的元素是ordered pairs, 我们就称G为digraph(有向图), 否则, 我们称G无无向图。
对于有向图, the pairs (u, v)和pairs (v, u) 是两个不同的边, 然而对于无向图, (u, v)和(v, u)是相同的边。 其中u, v是来自于节点集合V的
节点。
graph H是graph G 的子图 如果满足条件: 和。
也就是说,图H的每一个节点都是G的节点, 图H的每一个边都是G的边, 反之不成立。
下图表示无向图:
如下图有向图:
对于上面的的几个有向图, 我们得到如下的与图相对应的节点集合和边集合:
令 G是无向图, u 和 v是G的两个节点。 如果u 和 v 之间有一个边, 也就是(u, v)∈E, 我们就说u 和v 是邻接的(adjacent)。
如果一个边incident on a single vertex, 我们就说这事一个loop。 如果两个边e1, e2是由两个相同的节点{u, v} 组成, 我们就说e1 和 e2 是parallel edges。
一个graph 如果没有loops和parallel edges,我们就称这个图为simple graph(简单图)。e = (u, v) 是G的一个边, 我们就说边e is incident on the vertices
u and v。 节点u的度(degree), 记为deg(u), 或者d(u), 定义为incident with u的边的数目。 如果u有一个loop, 那么就会对该节点的度贡献2。 如果deg(u)的值为
偶数(奇数), 我们就称u 是偶的(奇的)。
如果经过一个节点序列u1, u2, ...un, 其中u= u1, un = v, (ui, ui+1)是一个边, 其中i = 1, 2, 3, ...n-1, 我们就说节点u 和节点v 之间有一条路径(path)。
或者说节点u 和v 是相连的(connected)。一个simple path的所有除了初始和最后一个节点外, 之间的节点都是distinct的, 我们就说这事一个simple
path。 一个cycle 是G中的一个simple path, 尽管最后一个节点和起始节点是相同的。
如果G中的任何两个节点之间都有一个path, 我们就说G是connected的。 A maximal subset(极大子集合) of connected vertices is called a component
of G。
对于当G是digraph 的时候, 如果(u, v) ∈E, 我们就说u is adjacent to v and v is adjacent from u。 path和cycles 的定义和无向图中的定义相似。
如果任何两个节点是connected, 我们就说图G是strongly connected。
例如, 上图中, 在G1中, 1-4-5 是from vertex 1 to vertex5的一个path。 G1中没有cycles. 在G2中, 1-2-1是一个cycles。 在G3中, 0-1-2-4-3是一个from vertex 1 to vertex 5的path。 等等, G3中也没有cycles.
Graph representation
为了编写程序处理图, 我们的图必须存储在计算机内存中。 也就是说, 必须表示出我们的graph.
主要有两种表示图的方法, 一种是使用邻接矩阵(adjacency matrices), 另一种是Adjacency Lists。
(1)Adjacency Matrices(邻接矩阵)
假设我们的graph 具有n个vertices: V(G) = {v1, v2, ..., vn}。这个图可以用邻接矩阵表示出来。 那么我们的邻接矩阵就是一个n x n的二维矩阵, 记为AG。
如果from vi to vj 之间有一个edge, 我们就记矩阵AG的(i,j)元素为1, 否则, 两个节点没有边直接相连, 我们记(i, j)元素为0。
数学表达式如下:
对于无向图, 不难看出, 只要(vi, vj)∈E(G), 就有(vj, vi)∈E(G), 也就是说, AG(i, j) = AG(j, i)。 所以, 多余无向图来说, 与之对应的邻接矩阵是对称的(symmetric).。
例如:下面的两个graph 对应的邻接矩阵:
下面给出使用邻接矩阵表示图的有缺点:
Pros:
representations is easier to implement(实现起来更容易), 去掉一个边仅需要花费的时间我O(1)(常数时间)。
查找节点(u,v) 之间时候有一个边, 仅需要花费O(1)(常数时间)。
Cons:需要花费的内存空间太大, 需要O(V^2)。 我们的图是sparse的时候, 也需要耗费相同的内存。 另外添加一个新的Vertex到图中, 需要花费的时间为O(V^2)。
为了克服邻接矩阵耗费内存的缺点, 下面给出图的另一种表示方法邻接链表(Adjacency Lists)。
Adjacency List.
假设我们的graph 具有n个vertices: V(G) = {v1, v2, ..., vn}。这个图可以用邻接链表表示出来。
对应于G的每一个vertices v, 都对应一个linked list, 这个linked list的每一个node 都包含一个节点u, 满足 (v, u)∈E(G)。 也就是说这个linked list 中存贮这所有与这个节点(vertices)对应的相邻的(adjacent) vertices。 由于共有n个vertices, 所以我们需要一个size 为n 的数组A, such that A[i] 是一个Reference variable(别名变量) pointing to the first node of the linked list containing the vertices to which vi is adjacent。 很明显, linked list 的每一个node 都有两个components, 即vertex 和link。 the component vertex 含有与vertex i 邻接的vertex。
举个例子:
下面的graph对应的Adjacency list: