前面我们介绍了图的一些基本概念,我们接着来看如何在程序中对图结构进行表示,这一部分可能会涉及到某些在《线性代数》这门课程中出现的概念。
邻接矩阵
有向图邻接矩阵
邻接矩阵实际上就是用矩阵去表示图中各顶点之间的邻接关系和权值。假设有一个图 $G=(V,E)$,其中有N个顶点,那么我们就可以使用一个N×N的矩阵来表示,比如下面有A、B、C、D四个顶点的图:

此时我们需要使用邻接矩阵来表示它,就像下面这样:

对于一个不带权值的图来说:
$$G_{ij} = \begin{cases} 1, & \text{无向图的 } (v_i, v_j) \text{ 或有向图的 } <v_i, v_j> \text{ 是图中的边} \\ 0, & \text{无向图的 } (v_i, v_j) \text{ 或有向图的 } <v_i, v_j> \text{ 不是图中的边} \end{cases} $$
对于一个带权值的图来说,如果有边,则直接填写对应边的权值,如果没有,那么就填写0或是∞(因为某些图会认为0也是权值,所以说可以用∞,它可以是一个计算机允许的最大值大于所有边的权值的数)来进行表示:
$$G_{ij} = \begin{cases} w_{ij}, & \text{无向图的 } (v_i, v_j) \text{ 或有向图的 } <v_i, v_j> \text{ 是图中的边} \\ 0 \text{ 或 } \infty, & \text{无向图的 } (v_i, v_j) \text{ 或有向图的 } <v_i, v_j> \text{ 不是图中的边} \end{cases} $$
所以说,对于上面的有向图,我们应该像这样填写:

无向图邻接矩阵
那么我们来看看无向图的邻接矩阵呢?比如下面的这个图:

对于无向图来说,一条边两边是相互连接的,所以说,A连接B,那么B也连接A,所以说就像这样:

所以说无向图的邻接矩阵必定是一个对称矩阵。
性质
我们可以来总结一下性质:
- 无向图的邻接矩阵一定是一个对称矩阵,因此,有时为了节省时间,我们可以只存放上半部分。
- 对于无向图,邻接矩阵的第
i行非0(或非∞)的个数就是第i个顶点的度。 - 对于有向图,邻接矩阵的第
i行非0(或非∞)的个数就是第i个顶点的出度(纵向就是入度了)
代码实现
结构体定义
首先我们需要对结构体进行一下定义,这里我们以有向图为例:
1 |
|
接着我们可以对其进行一下初始化创建后返回:
1 | Graph create(){ //创建时,我们可以指定图中初始有多少个结点 |
添加操作
接着我们就可以编写一下添加顶点和添加边的函数了:
1 | void addVertex(Graph graph, E element){ //添加顶点 |
案例
我们来尝试构建一下这个有向图:

1 | Graph graph = create(); |
接着我们打印此领接矩阵,看看是否变成了我们预想中的那样:
1 | void printGraph(Graph graph){ |
最后得到:

可以看到结果跟我们上面推导得出的邻接矩阵一模一样,当然这里仅仅是演示了普通的有向图,我们也可以稍微将代码进行修改,将其变成一个无向图或是带权有向图
邻接表
什么是邻接表
前面我们介绍了领接矩阵,我们可以使用邻接矩阵在程序中保存一个图的边相关信息,它采用二维数组的形式,将对应边的连接关系进行存储,但是我们知道,数组存在容量上的局限性(学了这么多节课了,应该能体会到,数组需要一段连续空间,无论是申请还是用起来都很麻烦)同时,我们创建邻接矩阵后,如果图的边数较多(稠密图)利用率还是挺高的,但是一旦遇到边数很少的图(稀疏图)那么表中大量的位置实际上都是0,根本没有被利用起来,是很浪费的。
此时,我们可以考虑使用链式结构来解决这种问题,就像下面这样:

对于图中的每个顶点,建立一个数组,存放一个头结点,我们将与其邻接的顶点,通过一个链表进行记录(看着挺像前面讲的哈希表)这样,也可以表示一个图的连接关系,并且内存空间能够得到更加有效的利用。当然,对于无向图来说,跟之前一样,两边都需要进行保存:

代码操作
初始化
我们来尝试编写一下代码实现,首先还是定义:
1 |
|
接着是对其进行初始化:
1 | Graph create(){ //创建时,我们可以指定图中初始有多少个结点 |
添加边和顶点
1 | void addVertex(Graph graph, E element){ |
案例
我们来将其构建一下吧,还是以上面的图为例:

1 | int main(){ |
我们来打印看看效果:
1 | void printGraph(Graph graph){ |
得到结果如下:

逆邻接表
虽然邻接表的方式看上去更加的简单高效,但是会给我们带来一些不必要的麻烦,比如上面创建的领接表,我们只能快速得到某个顶点指向了哪些顶点,也就是只能计算到顶点的出度,但是无法快速计算顶点的入度,只能将所有结点统计之后才能得到入度。所以说在表示有向图时,查找上并没有邻接矩阵来的方便。
为了解决这种问题,我们可以建立一个逆领接表,来表示所有指向当前顶点的顶点列表:

实际上就是反着来而已,通过建立这两个领接表,就能在一定程度上缓解不方便的情况。
说些什么吧!