第二十二章 图的基本算法
总结:这一章讲了图的一些基本算法,例如广度优先搜索、深度优先搜索等,还介绍了拓扑排序、强连通分支等。
1. 图的表示
G=(V,E)
表示方法:邻接表、邻接矩阵
邻接表:存储空间O(V+E)
邻接矩阵:适合稠密图,存储空间O(V2)。还有一个优点,对邻接矩阵中的元素,可用二进位表示。
2. 广度优先搜索BFS
对图中的顶点,从源顶点出发,先访问源顶点可以到达的每个顶点,再对这些顶点,依次访问它们可以到达的每个顶点,这样依次一圈圈访问下去,按广度搜索下去。
对V中的每个顶点u,有color[u]代表它的访问状态(白色,未访问;灰色,发现并访问中;黑色,访问完),pi[u]代表它在生成广度优先树中的父母,d[u]代表u与源顶点s之间的最短距离。
总运行时间:O(V+E)
BFS(G,s)
for each vetex uЄV(G)-{s}
do color[u] <- WHITE
pi[u] <- NIL
d[u] <- 无穷大
color[s] <- GRAY
d[s] <- 0
pi[s] <- NIL
Q <- 空队列
ENQUEUE(Q,s)
while Q!=空
do u <- DEQUEUE(Q)
for each vЄAdj[u]
do if color[v]=WHITE
then d[v] <- d[u]+1
pi[v] <- u
color[v] <- GRAY
ENQUEUE(Q,v)
color[u] <- BLACK
打印从s到v的最短路径。s到v的最短路径就是s到pi[v]的最短路径,加上pi[v]到v的最短路径。
伪代码
PRINT-PATH(G,s,v)
if v=s
then print s
else if pi[v]=NIL
then print “no path from ” s “ to ” v “ exists.”
else PRINT-PATH(G,s,pi[v])
print v
3. 深度优先搜索DFS
对最新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续探测下去。当顶点v的所有边都已被探测过后,探索将回溯到发现顶点v有起始点的那些边。这一过程一直进行到已发现从源顶点可达的所有顶点时为止。如果还存在未被发现的顶点,则选择其中一个作为源顶点,并重复以上过程。
对每个顶点u,color[u]代表访问状态(白色,未发现;灰色,被发现;黑色,结束时)。d[u]代表发现u的时间,f[u]代表完成对u的探索时的时间。
总运行时间:O(V+E)
伪代码
DFS(G)
for each vertex uЄV(G)
do color[u] <- WHITE
pi[u] <- NIL
time <- 0
for each vertex uЄV(G)
do if color[u]=WHITE
then DFS-VISIT(u)
DFS-VISIT(u)
time <- time+1
d[u] <- time
color[u] <- GRAY
for each vertex vЄAdj[u]
do if color[v]=WHITE
then pi[v] <- u
DFS-VISIT(v)
color[u] <- BLACK
time <- time+1
f[u] <- time
深度优先搜索的性质:
1. 它的先辈子图形成了一个由深度优先树组成的深度优先森林
2. 括号定理
根据由图生成的深度优先森林,可以把图的边分类:树边、反向边、正向边、交叉边
4. 拓扑排序
对有向无回路图G=(V,E)进行拓扑排序后,结果为该图所有顶点的一个线性序列,满足若G包含边(u,v),则在该序列中,u就出现在v的前面。一个图的拓扑排列可以看成是图中所有顶点沿水平线排列而成的一个序列,使得所有的有向边均从左指向右。
即将v按f[v]从大到小排列
总运行时间:O(V+E)
伪代码
TOPOLOGICAL(G)
call DFS(G) to compute f[v] for each vertex v
as each vertex if finished, insert it onto the front of a linked list
return the linked list of vertices
5. 强连通分支
强连通分支就是一个最大的顶点集合C,对与C中的每一对顶点u和v,u和v都是互相可达的。
算法思路:
DFS(G)计算出G中每个顶点u的f[u],计算GT,对GT调用DFS,且对源顶点的访问顺序根据f[u]的大小,从大到小的进行。最后,获得的深度优先森林中的各深度优先树,就是图的各个强连通分支。