一、图的概念和术语
1、图的定义
图:是由两个集合 V(G)和 E(G)组成的,记为 G=(V,E),其中:V(G)是顶点的非空有限集,E(G)是边的有限集合,边是顶点的无序对或有序对
注意:线性表可以是空表,树可以是空树,但图不可以是空图。就是说,图中不能一个顶点也没有,图的顶点集V一定非空,但边集E可以为空,此时图中只有顶点而没有边。
2、图的其他概念
-
有向图:图是由顶点的有穷非空集合和顶点之间边的集合组成, 通常表示为: G(V,E), 其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
-
无向图:无向图 G 是由两个集合 V(G)和 E(G)组成的,其中:V(G)是顶点的非空有限集,E(G)是边的有限集合,边是顶点的无序对,记为(v,w)或(w,v),并且(v,w)=(w,v),在一个具有n个顶点的无向图中,要连通全部顶点至少需要n-1条边
-
有向完全图—图中任意两个不同顶点之间都有一条弧,n 个顶点的有向图最大边数是 n(n-1),边数e∈[0,n(n-1)]
-
无向完全图——图中任意两个不同顶点之间都有一条无向边,n 个顶点的无向图最大边数是 n(n-1)/2,边数为e∈[0,n(n-1)/2]
-
权——与图的边或弧相关的数叫权
-
网——带权的图叫网
-
顶点的度:无向图中,顶点的度为与每个顶点相连的边数;在无向图中,所有顶点度的和是图当中边的两倍;有向图中,顶点的度分成入度与出度,入度:以该顶点为头的弧的数目出度:以该顶点为尾的弧的数目
-
顶点的度之和 = 2 * 顶点入度之和 = 2*顶点出度之和 = 顶点入度之和+顶点出度之和=边数的两倍
-
路径长度——沿路径边的数目或沿路径各边权值之和
-
回路——第一个顶点和最后一个顶点相同的路径叫回路
-
简单路径——序列中顶点不重复出现的路径叫简单路径
-
简单环——除了第一个顶点和最后一个顶点外,其余顶点不重复出现的回路叫简单环
-
连通——从顶点 V 到顶点 W 有一条路径,则说 V 和 W 是连通的
-
连通图——图中任意两个顶点都是连通的叫连通图
-
连通分量——非连通图的每一个连通部分叫连通分量
-
强连通图——有向图中,如果对每一对 Vi,Vj∈V, Vi≠Vj,从 Vi 到 Vj 和从 Vj 到 Vi 都存在路径,则称 G 是强连通图,否则被称为非强连通图。
-
生成树——在一个有 n 个顶点的连通图 G 中,存在一个极小的连通子图G',G'包含图 G 的所有顶点,但只有 n-1 条边,并且G’是连通的,则称G’为图 G的生成树。注意:一个连通图的生成树不是唯一的。在一个棵生成树上添加一条边,一定构成环路。非连通图的生成树的组成一个生成森林
-
有向图的生成森林是这样一个子图,由若干棵有向树组成,含有图中全部顶点。
4、有向图邻接矩阵的特性
-
-
对于顶点 vi,第 i 行的非 0 元素的个数是其出度 OD(vi)
-
第 i 列的非 0 元素的个数是其入度 ID(vi)
-
邻接矩阵中非 0 元素的个数就是图的弧的数目
-
二、图的存储结构
1、邻接矩阵(数组)表示法
基本思想:对于有 n 个顶点的图,用一维数组 vexs[n]存储顶点信息,用二维数组 A[n][n]存储顶点之间关系的信息。该二维数组称为邻接矩阵。在邻接矩阵中,以顶点在 vexs 数组中的下标代表顶点,邻接矩阵中的元素 A[i][j]存放的是顶点 i 到顶点 j 之间关系的信息。
(1)、无向图的数组表示
a、无向无权图的数组存储
b、无向带权图的数组存储
无向图邻接矩阵的特性:
-
邻接矩阵是对称方阵
-
对于顶点 vi,其度数是第 i 行的非 0 元素的个数
-
无向图的边数是上(或下)三角形矩阵中非 0 元素个数
(2)、有向图的邻接矩阵
a、带权图的邻接矩阵
有向图邻接矩阵的特性
-
对于顶点 vi,第 i 行的非 0 元素的个数是其出度 OD(vi);第 i 列的非 0 元素的个数是其入度 ID(vi) 。
-
邻接矩阵中非 0 元素的个数就是图的弧的数目。
2、邻接表
3、十字链表表示法
有向图的十字链表表示法
4、邻接多重表表示法
三、图的遍历
1、广度优先遍历BFS
广度优先搜索是按层来处理顶点,距离开始点最近的那些顶点首先被访问,而最远的那些顶点则最后被访问,这个和树的层序变量很像,BFS的代码使用了一个队列。搜索步骤:
a .首先选择一个顶点作为起始顶点,并将其染成灰色,其余顶点为白色。
b. 将起始顶点放入队列中。
c. 从队列首部选出一个顶点,并找出所有与之邻接的顶点,将找到的邻接顶点放入队列尾部,将已访问过顶点涂成黑色,没访问过的顶点是白色。如果顶点的颜色是灰色,表示已经发现并且放入了队列,如果顶点的颜色是白色,表示还没有发现
d. 按照同样的方法处理队列中的下一个顶点。
基本就是出队的顶点变成黑色,在队列里的是灰色,还没入队的是白色。
用一副图来表达这个流程如下:
1.初始状态,从顶点1开始,队列={1}
2.访问1的邻接顶点,1出队变黑,2,3入队,队列={2,3,}
3.访问2的邻接顶点,2出队,4入队,队列={3,4}
4.访问3的邻接顶点,3出队,队列={4}
5.访问4的邻接顶点,4出队,队列={ 空}
分析:
从顶点1开始进行广度优先搜索:
初始状态,从顶点1开始,队列={1}
访问1的邻接顶点,1出队变黑,2,3入队,队列={2,3,}
访问2的邻接顶点,2出队,4入队,队列={3,4}
访问3的邻接顶点,3出队,队列={4}
访问4的邻接顶点,4出队,队列={ 空}
顶点5对于1来说不可达。
2、深度优先遍历DFS
深度优先遍历主要思路是从图中一个未访问的顶点 V 开始,沿着一条路一直走到底,然后从这条路尽头的节点回退到上一个节点,再从另一条路开始走到底...,不断递归重复此过程,直到所有的顶点都遍历完成,它的特点是不撞南墙不回头,先走完一条路,再换一条路继续走。
1、我们从根节点 1 开始遍历,它相邻的节点有 2,3,4,先遍历节点 2,再遍历 2 的子节点 5,然后再遍历 5 的子节点 9。
2、上图中一条路已经走到底了(9是叶子节点,再无可遍历的节点),此时就从 9 回退到上一个节点 5,看下节点 5 是否还有除 9 以外的节点,没有继续回退到 2,2 也没有除 5 以外的节点,回退到 1,1 有除 2 以外的节点 3,所以从节点 3 开始进行深度优先遍历,如下:
3、同理从 10 开始往上回溯到 6, 6 没有除 10 以外的子节点,再往上回溯,发现 3 有除 6 以外的子点 7,所以此时会遍历 7
4、从 7 往上回溯到 3, 1,发现 1 还有节点 4 未遍历,所以此时沿着 4, 8 进行遍历,这样就遍历完成了
完整的节点的遍历顺序如下(节点上的的蓝色数字代表)
四、图的连通性
1、生成树
所有顶点均由边连接在一起,但不存在回路的图叫生成树,深度优先生成树与广度优先生成树生成森林:非连通图每个连通分量的生成树一起组成非连通图的生成森林。一个图可以有许多棵不同的生成树
生成树特点:
-
生成树的顶点个数与图的顶点个数相同
-
生成树是图的极小连通子图
-
一个有 n 个顶点的连通图的生成树有 n-1 条边
-
生成树中任意两个顶点间的路径是唯一的
-
在生成树中再加一条边必然形成回路
-
含 n 个顶点 n-1 条边的图不一定是生成
2、最小生成树
生成树的每条边上的权值之和最小代价生成树
3、无向图的连通分量
在对无向图进行遍历时,对于连通图,仅需从图中任一顶点出发,进行深度优先搜索或广度优先搜索,便可访问到图中所有顶点。对非连通图,则需从多个顶点出发进行搜索,而每一次从一个新的其实点出发进行搜索过程中得到的顶点访问序列恰为其各个连通分量中的顶点集
4、有向图的强连通分量
强连通(strongly connected): 在一个有向图G里,设两个点 a b 发现,由a有一条路可以走到b,由b又有一条路可以走到a,我们就叫这两个顶点(a,b)强连通。
5、强连通图
如果 在一个有向图G中,每两个点都强连通,我们就叫这个图,强连通图。
6、强连通分量
在一个有向图G中,有一个子图,这个子图每2个点都满足强连通,我们就叫这个子图叫做 强连通分量 (分量:把一个向量分解成几个方向的向量的和,那些方向上的向量就叫做该向量(未分解前的向量)的分量)
7、构造最小生成树方法
(1)普里姆(Prim)算法
以顶点为基准去找最短权值的边
(2)克鲁斯卡尔(Kruskal)算法
以边为基准出大搜索找顶点
五、拓扑排序
1、拓扑排序的方法
-
在有向图中选一个没有前驱的顶点且输出之
-
从图中删除该顶点和所有以它为尾的弧
注:拓扑排序方法不唯一
重复上述两步,直至全部顶点均已输出;或者当图中不存在无前驱的顶点为止
六、关键路径
定义:路径上各活动持续时间之和最长的路径叫做关键路径