数据结构进阶实训十二 图的存储结构
Data structure advanced training course notes and algorithm exercises 数据结构进阶实训课程笔记和算法练习
1.图的定义
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V, E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
对于图的定义,需要注意的几个地方:
- 线性表中把数据元素叫元素,树中将数据元素叫结点,图中将数据元素称之为顶点(Vertex)。
- 线性表中可以没有数据元素,称之为空表。树中可以没有结点,叫做空树。但在图结构中,不允许没有顶点。在定义中,若V是顶点的集合,则强调顶点集合V有穷非空。
- 线性表中,相邻的元素之间具有线性关系,树结构中,相邻两层的结点具有层次关系,而图中,任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的。
1.1 各种图定义
- 无向边:若顶点到之间的边没有方向,则称这条边为无向边(Edge),用无序偶对来表示。
- 无向图:图中任意两顶点之间的边都是无向边。
- 有向图:若从顶点到之间的边有方向,则称这条边为有向边(Edge),也称为弧(Arc)。用有序偶来表示,称为弧尾(Tail),称为弧头(Head)。
- 有向图:图中任意两个顶点之间的边都是有向边。
- 在图中,若不存在顶点到其自身的边,且同一条边不重复出现,则称这样的图为简单图。即不存在自环和重复边。
- 无向完全图:在无向图中,如果任意两顶点之间都存在边。含有n个顶点的无向完全图有条边。
- 有向完全图:在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧。含有n个顶点的有向完全图有条边。
- 有很少条边或弧的图称为稀疏图反之称之为稠密图。
- 有些图的边或弧具有与它相关的数字,这种与图的边或弧相关的数叫做权(Weight)。这种带权的图通常称为网(Network)。
- 假设有两个图和,如果且,则称为的子图(Subgraph)。
1.2 连通图相关术语
在无向图G中,如果从顶点到顶点有路径,则称和是连通的。如果对于图中任意两个顶点,和都是连通的,则称G是连通图(Connected Graph)。
无向图中的极大连通子图称为连通分量。
在有向图G中,如果对于每一对,从到和从到都存在路径,则称G是强连通图。有向图中的极大强连通子图称做有向图的强连通分量。
- 一个连通图的生成树是一个极小的连通子图,它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边。
- 如果一个有向图恰有一个顶点的入度为0,其余顶点的入度均为1,则是一棵有向树。
- 一个有向图的生成森林由若干棵有向树组成,含有图中全部顶点,但只有足以构成若干棵不相交的有向树的弧。
2.图的存储结构
图的存储方式一般有两类,用边的集合方式有邻接矩阵,用链式方式有邻接链表、十字链表、邻接多重表、边集数组等。
2.1 邻接矩阵
图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。
代码实现:
1 | typedef char VertexType; /* 顶点类型应由用户定义 */ |
无向网图的创建代码:
1 | /* 建立无向图的邻接矩阵表示 */ |
从代码中可以得到,n个顶点和e条边的无向网图的创建,时间复杂度为,其中对邻接矩阵Garc的初始化耗费了的时间。
2.2 邻接表
数组与链表相结合的存储方法称为邻接表(Adjacency List)。图中顶点用一个一维数组存储,每个数据元素还需要存储指向第一个邻接点的指针,以便于查找该顶点的边信息。
代码实现:
1 | typedef char VertexType; /* 顶点类型由用户定义 */ |
无向图的邻接表创建代码如下:
1 | /* 建立图的邻接表结构 */ |
这里采用头插法来建立两顶点间关系,对于n个顶点e条边来说,很容易得出算法的时间复杂度是。
2.3 图的基本操作
- 为实现遍历必须设置访问标志数组,以防止走回路或未访问到。
- 图的遍历规律有两种:深度优先遍历DFS和广度优先遍历BFS。可用邻接矩阵和邻接表实现。
- DFS算法是以递归技术为支持,BFS算法是以队列技术为支持。
2.4 图的应用
图的遍历算法是图应用的重要基础。
求解生成树、最小生成树、连通分量、拓扑排序、关键路径、单源最短路径及所有顶点之间的最短路径的重要算法应用。
3.建立图的邻接矩阵存储
3.1 有向图,无向图,有向网,无向网
1 |
|
4.邻接矩阵的深度和广度优先搜索
1 | 写出上述建立图的深度和广度优先搜索序列。 |
4.1 算法设计思想
深度优先搜索
深度优先搜索的过程类似于树的先序遍历
所谓深度优先搜索,是从图中的一个顶点出发,每次遍历当前访问顶点的临界点,一直到访问的顶点没有未被访问过的临界点为止。然后采用依次回退的方式,查看来的路上每一个顶点是否有其它未被访问的临界点。访问完成后,判断图中的顶点是否已经全部遍历完成,如果没有,以未访问的顶点为起始点,重复上述过程。
深度优先搜索是一个不断回溯的过程。
广度优先搜索
广度优先搜索类似于树的层次遍历
从图中的某一顶点出发,遍历每一个顶点时,依次遍历其所有的邻接点,然后再从这些邻接点出发,同样依次访问它们的邻接点。按照此过程,直到图中所有被访问过的顶点的邻接点都被访问到。
最后还需要做的操作就是查看图中是否存在尚未被访问的顶点,若有,则以该顶点为起始点,重复上述遍历的过程。
4.2 源代码
1 | typedef enum{false,true}bool; //定义bool型常量 |
4.3 运行情况截图
以下演示的是图采用邻接矩阵存储结构的有向图和无向图的建立。
5.建立图的邻接表存储
5.1 有向图,无向图,有向网,无向网
1 |
|
6.邻接表的广度和深度优先搜索
1 | 写出上述建立图的深度和广度优先搜索序列。 |
6.1 源代码
1 | //DFS遍历 |
6.2 运行截图
以下演示的是图采用邻接表存储结构的无向图和有向网的建立。