数据结构之图-图的存储结构-学习笔记-64

引入

学了那么多图的知识,是时候动手设计一下图的存储结构了,图跟线性表、树有那么大得差别,所以图的数据结构要复杂很多。

思考

我们先回忆一下线性表和树得存储结构

  • 线性表:一对一的关系,用数组和链表就能很好的表示。
  • 树:一对多得关系,用数组和链表得特性结合在一起就可以很好的表示。

那么图呢?

  • 图上的任意一个顶点都可以是第一个顶点,谁开始都行。
  • 任意一个顶点的邻接点也不存在次序关系,多对多,大家都一样。

我们观察下面的四张图

这四张图形态各异,但是

  • 各个结点的邻接点都相同
  • 边也都相同
  • 所以位置虽然不一样,但其实都是一张图。

那么该如何将图在内存中表示呢?

  • 既然任意两个顶点都可能存在联系
  • 我们不能线性的表达数据元素之间得关系
  • 因为内存的物理位置也是线性的,而图的元素关系是平面的。
  • 既然线性关系不行,那用多重链表呢?可以!
  • 但会造成巨大的资源浪费,各个顶点得度数如果相差很大,就会造成很大的浪费。

解决

想明白上面得问题,我们就来回答这个问题

  • 图是由顶点和边两部分组成的,合在一起表示困难,我们分开表示就好了
  • 顶点:因为不分大小、主次,用一个简单的一维数组来存储就好
  • 边、弧:其实就是顶点和顶点之间得关系,一个二维数组来存储就好
  • 所以,我们使用邻接矩阵方案,撒花!

无向图的邻接矩阵

图的邻接矩阵(Adjacency Matrix):存储方式是用两个数组来表示图。

  • 一个一维数组存储图中得顶点信息
  • 一个二维数组(邻接矩阵)存储图中边、弧的信息

我们试着来存放上面这个无向图,首先设计一个一维数组 vexs 存放顶点信息

vexs0 vexs1 vexs2 vexs3
V0 V1 V2 V3

接着设计一个邻接矩阵(二维数组)arcs,存放边和边得信息

V0 V1 v2 v3
V0 0 1 1 1
V1 1 0 1 0
V2 1 1 0 1
V3 1 0 1 0

因为是无向图,所以

  • 无论是先行再列看(V0,V1)还是 先列再行看(V1,V0)都可以
  • 邻接矩阵存储得是边、弧,如果存在就写1,反之则写0
  • 这个邻接矩阵也是一个对称矩阵,因为这是一个无向图。

无向图邻接矩阵的存储结构

我们设计这个存储结构时

  • 设计两个数组
  • 顶点数组vertex[4]
  • 边数组 arc[4][4] 为对称矩阵
  • 边数组中,0表示顶点间不存在边,1表示顶点间存在边

所谓对称矩阵,就是

  • N 阶矩阵的元满足
  • a[i][j]=a[j]i
  • 也就是从矩阵的左上角到右下角为轴
  • 右上角得元与左下角相对应得元全都是相等的

有了这个数据结构,我们就可以很轻松的知道

  • 任意两个顶点之间是否存在边,0就是没边,1就是有边
  • 某个顶点的度,就是顶点 Vi 在邻接矩阵中第 i 行或 i 列的元素的和。
  • 还记得无向图顶点的度嘛?就是该顶点邻接的边的个数
  • 就是这一列或这一行,有多少个 1,加起来就是这个 Vi 的度
  • 最后一个就是求 Vi 顶点的所有邻接点
  • 就是将第 i 行元素扫描一遍,只要 arc[ i ][ j ]为1就是邻接点
V0 V1 v2 v3
V0 0 1 1 1
V1 1 0 1 0
V2 1 1 0 1
V3 1 0 1 0

V1 的度是2,因为无论行、列,为1的元素加起来,都是2
V1 的邻接点是 V0、V2,因为 V1 行只有 V0、V2 的元素为1

我们看看图对不对,没有问题!

有向图的邻接矩阵

我们在无向图中发现,邻接矩阵竟然是对称的,原因也很简单

  • 因为无论是顶点 A 到顶点 B 还是顶点 B 到顶点 A 都是相同的
  • 那么在有向图中,给顶点规定了方向之后,会不会好一些呢?

有向图最重要的一点就是区分方向,我们按照上面的思路,首先设计一个一维数组 vexs 存放顶点信息

vexs0 vexs1 vexs2 vexs3
V0 V1 V2 V3

接着设计一个邻接矩阵(二维数组)arcs,存放边和边得信息

V0 V1 v2 v3
V0 0 0 0 1
V1 1 0 1 0
V2 1 1 0 0
V3 0 0 0 0

因为我们要注意方向

  • 所以行为弧为、列为弧头
  • 如果不规定,那么 A->B 和 B->A 就一样了
  • 这就不是有向图了

有向图邻接矩阵的存储结构

我们设计这个存储结构时

  • 设计两个数组
  • 顶点数组vertex[4]
  • 边数组 arc[4][4] ,有向图矩阵并不对称
  • 边数组中,0表示顶点间不存在边,1表示顶点间存在边

有向图有入度和出度的概念,所以

  • 顶点 V1 的入度为1,正好是第 V1列的各数之和
  • 顶点 V1 的出度为2,正好是第 V2行的各数之和
  • 我们看看下表
V0 V1 v2 v3
V0 0 0 0 1
V1 1 0 1 0
V2 1 1 0 0
V3 0 0 0 0

V1 的入度是1,出度是2

  • V1列所有元素的和为1
  • V1行所有元素的和为2
  • 我们看下图验证一下

没有问题,我们接下来实际设计一下数据结构。

邻接矩阵(网)

我们在上一节,说到了网这个概念,实际上就是每条边上带有权的图,就是网。

  • 网跟上面的思路一样,使用两个数组搞定
  • 在数组中的元素就不在是0和1了
  • 因为每条边有权,所以我们就将每条边的权值赋值到二维数组中

网最重要的一点就是看好权值,我们按照上面的思路,首先设计一个一维数组 vexs 存放顶点信息

vexs0 vexs1 vexs2 vexs3
V0 V1 V2 V3

接着设计一个邻接矩阵(二维数组)arcs,存放边和边得信息

V0 V1 v2 v3
V0 $\infty$ $\infty$ $\infty$ 18
V1 8 $\infty$ 2 $\infty$
V2 4 $\infty$ $\infty$ $\infty$
V3 $\infty$ $\infty$ $\infty$ $\infty$

这里有三种情况

  • 如果有弧,则将权值写入
  • 如果指向自己,那就写$\infty$
  • 如果没有弧,那就写入$\infty$
  • $\infty$ 表示计算机允许的,一个大于所有权值的值。

代码实现

1
2
3
4
5
6
7
typedef enum { DG, UDG } GraphType; //声明图的类型,有向还是无向
typedef struct {
char vexs[MAX_VEX_NUM]; //一维数组,用于存放顶点
int arcs[MAX_VEX_NUM][MAX_VEX_NUM]; //二维邻接矩阵,存放边、弧
int vexnum, arcnum; //存放顶点数量和边、弧的数量
GraphType type; //从有向和无向中选择一个
} MGraph;

好了,理解了上述概念,我们下节课来讲,如何创建一个图。

尾巴

这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。


-------------The End-------------
欢迎请我喝咖啡哦~!