文章目录
图遍历(Graph Traversal)
一、深度优先搜索(Depth-First Search)
深度优先搜索如下图所示:
树边:深度优先搜索树的边
回边:所有其他边
算法伪代码如下:
Input: 无向图G=(V, E);
Output: 对应深度优先搜索树中顶点的预排序和后排序
predfn = 0
postfn = 0
for 每个点v属于V:
标记v为unvisited
for 每个点v属于V://防止存在不连通图
if v is unvisited:
dfs(v)
def dfs(v):
标记v为visited
predfn = predfn + 1//节点搜索顺序
for 每条边(v,w)属于E:
if w is unvisited:
dfs(w)
postfn = postfn + 1//回溯顺序
搜索完成后,每个顶点都被predfn和postfn标记,它们给出了访问顶点的开始和结束的顺序。
假设图G的节点数为n,边数为m。
对于for 每个点v属于V中的if v is unvisited ,在循环内的时间复杂度为O(1),在算法执行结束后遍历每个节点,故为O(n)。
对于dfs(v)中的for 每条边(v,w)属于E ,算法执行结束后运行的次数为O(m)。对于有向图是m次,对于无向图是2m次。
综上,对于for 每个点v属于V://防止存在不连通图 ,算法执行结束后循环了O(m+n)次,故算法的时间复杂度为O(m+n)。
二、寻找关节点(Finding Articulation Points in a Graph)
寻找图中关节点是深度优先搜索的一个简单应用。
对于无向图G,存在一个顶点v,如果有不同于v的两个顶点u和w,在它们之间的任何路径都必须经过顶点v,则称v为关节点。即如果G是连通图,移去v和他相关的边,将会变得不连通。
算法思想:
首先对G进行一次深度搜索遍历,遍历过程中对每个顶点v都保持两个标号:A[v]、B[v],其中A[v]为predfn,B[v]为以下三个中的最小值:
- A[v]
- A[u],(v,u)为回边
- B[w],在深度优先搜索树中的每条边(v,w)
关节点确定如下:
- 根是关节点当且仅当在深度优先搜索他有两个或多个儿子(不算回边);
- 除根以外的节点v是关节点当且仅当v有一个儿子使得B[w]>=A[v]。
算法伪代码如下:
Input: 无向图G=(V, E);
Output: 包含G的所有可能关节点数组A[1....count]
predfn = 0
rootdegree = 0//根节点的度
count = 0//关节点数
for 每个点v属于V:
标记v为unvisited
for 每个点v属于V://防止存在不连通图
if v is unvisited:
artpoint(v)
def artpoint(v):
标记v为visited
predfn = predfn + 1//节点搜索顺序
A[v] = predfn
B[v] = predfn
isart = false
for 每条边(v,w)属于E:
if (v,w)是树边:
if w is unvisited:
artpoint(w)
if v = s:
rootdegree++
if rootdegree == 2:
isart = true
else:
B[v] = min{B[v],B[w]}
if B[w] >= A[v]:
isart = true
else if (v,w)是回边:
B[v] = min{B[v],A[w]}
if isart:
count++
A[count] = v
在搜索从某个顶点w回退到v时,要做两条判断:
- 如果发现B[w]比B[v]小,B[v]被设为B[w]
- 如果发现B[w]比A[v]大或相等,那么此时v就是一个关节点,因为从w到v的祖先顶点必经过v。
从以w为根的子树到u的任何路径都需要经过v。在这个图中u是关节点,因为他的度大于1。
例子:
三、广度优先搜索(Depth-First Search)
广度优先搜索如下图所示:
这种遍历方法可以通过队列来实现,以存储未检查的顶点。