前言
由于图的术语比较多,大部分的术语和实际编程可能没有什么关系,所以我这里只记录有必要的术语,其他的术语我简单地列在最后。如果想要具体了解图的话,可以去看看《大话数据结构》这本书籍。然后我会分两篇博客来记录图,这篇博客主要记录图的两种存储结构,下一篇是有关图的各种算法。图也是我比较少用的数据结构,以前学的时候学不懂,现在回头再看,能够get到里面许多点了。
图的定义
图是一种比较复杂的数据结构,他的结点之间的关系可以是任意的,图中的任意两个数据元素之间都可能相关。下图展示了图的结构:
下面给出图的定义:
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V, E),其中G表示一个图,V是图G中顶点的集合,E是图G中的边的集合。
我们将图的数据元素称为顶点(Vertex),并且图的顶点集合一定是有穷且非空的,即图可以没有边,但一定不能没有顶点(至少有一个)。 在图中,任意两个顶点之间都可能有关系,顶点之间的关系用边来表示。
图的分类
图的分类有很多种,这里主要依据边的无向和有向,将图分为无向图和有向图。
- 无向边: 若顶点vi到vj之间没有方向,则称这条边为无向边(Edge),用无序偶对(vi, vj)来表示。
- 有向边:若顶点vi到vj之间有方向,则称这条边为有向边,也称为弧(Arc)。用有序偶对<vi, vj>来表示有向边,vj是弧头, vi是弧尾,即该边的方向是vi–> vj(注意不要把头和尾弄反了)。
别的分类还有简单图、复杂图、稀疏图、稠密图,具体参照书去了解。在存储结构上稍微有点区别的就只有无向图和有向图了,所以这里就抛开别的术语,单纯讲讲这两种图的区别。
无向图
顶点与顶点之间没有指向关系,或者说一条边描述了两个顶点间具有双向关系。具体的例子有朋友圈、地铁路线图等。下图为无向图:
有向图
顶点与顶点之间有明确的指向关系,或者说一条边只描述了两个顶点之间具有一个单向的关系。具体的例子有微博的关注列表、食物链、程序的函数依赖图等。下图为有向图:
图的两种存储结构
图相比起线性表和树,在存储方面上复杂了许多。如果说还像链表或者树那样,用一个结点和指向下一个结点的指针来作为顶点的结构,这是不合理的。因为一个图并没有明确的头结点或者根节点,顶点与顶点之间也没有明确的顺序关系或者层次关系,所以这样的存储结构是不合理的。
而且就算采用了这样的存储结构,由于顶点之间的关系是不明确的,一个顶点可能与多个顶点有关系,也可能没有关系。这样在设计下一个顶点指针的时候只能用一个可变数组来维护(有点类似与多叉树),但这样的设计不仅难以维护,操作起来也不太方便。
在前人的不屑努力下,总结出了五种比较适合图的存储结构。但这里只介绍两种比较常用的,感兴趣的话可以去参考书籍。下面罗列出这五种存储结构:
- 邻接矩阵
- 邻接表
- 十字链表
- 邻接多重表
- 边集数组
邻接矩阵
图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维数组存储图中的顶点信息,一个二维数组(称为邻接矩阵)来存储图中的便或弧的信息。 用上面的无向图做例子,那么他的存储结构应该是这样子的:
一维数组:
0 | 1 | 2 | 3 |
---|---|---|---|
A | B | C | D |
二维数组(邻接矩阵):
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0 | 1 | 1 | 1 |
1 | 1 | 0 | 1 | 0 |
2 | 1 | 1 | 0 | 1 |
3 | 1 | 0 | 1 | 0 |
一维数组是顶点到矩阵行列下标的一个映射,真正用来表示关系(边或弧)的是这个矩阵。
可以看到,在矩阵上面,行表示顶点,列表示边。比如A跟B、C、D有关系,所以0行上的1、2、3列被设为了1&#