4. 图搜索及其连通性
对于图来说,考虑的几个比较重要的因素就是连通性和路径;连通性关注的有强连通分量和特殊结构(例如web中的蝴蝶结构),路径关注的有两个节点间的最短路径和任意节点间的所有简单路径。
连通性和路径其实都是关于图中的搜索,通过图搜索希望能够找到目标节点,并且每个节点尽量访问一次(或者说复杂度是线性的)。
4.1 BFS和DFS
广度优先和深度优先是非常熟悉和基础的两个图搜索算法。
其中BFS采用FIFO以层的方式对图中节点进行访问,能够计算两点间最短路径和发现无向图中的连通分量;而DFS是回溯探索,能够计算有向图中的拓扑序和有向图中的连通分量。
BFS(Graph G,Start vertex s):
-- mark s as explored
-- initial Q as a queue, and push s into Q
-- while Q is not empty
-- pop a node v from Q
-- for each edge (v,w)
-- if w unexplored
-- mark w as explored
-- push w into Q
BFS可以访问从特定节点s到任意节点v的所有路径。其算法复杂度为O(m+n).(如果是用邻接矩阵来表示图G,算法复杂度就不是O(m+n),最差为O(n2))
利用BFS进行连通分量的计算,对每个节点进行BFS,只要进行一次,就是完成了一次连通分量访问的切换。
利用BFS进行最短路径的计算:
-- compute the shortest path from s to v, anddenote it as d(v)
-- initial d(v), if v==s d(v)=0, and if v isnot s d(v)=oo
-- initial Q as a queue, push s into Q
--while Q is not empty
-- pop a node w from Q
-- for each edge (s,w)
-- if w unexplored
--mark w as explored
--d(v)++
-- if w==v break
利用DFS可以计算拓扑排序和有向网络中的连通分量,其实现借助堆栈比较容易。
DFS(Graph G, start vertex s)(Recursive Version):
-- mark s as explored
-- for every edge (s,v)(for every edge which is connected to s)
-- if v unexplored
-- DFS(G,v)
4.2 拓扑排序和强连通分量
这两者都可以通过DFS来实现。
拓扑排序的定义:有向图G的拓扑排序指的是对G中的节点进行标注,并满足1.标注的值是从1到n;2.如果有有向边(u,v),则u的标注值小于v的标注值。
可以从上面的定义知道,如果有向图G中存在循环,是无法进行拓扑排序的;拓扑排序的一个应用是在流程先后顺序上,例如课程顺序安排中的先修和后续等。
进行拓扑排序的方案1:Streight-ForwardSolution:
-- letv be a sink vertex of G
-- set f(v)=n
-- recursive on G-v
注:对于任意有向无环图,至少存在一个sink节点,所谓sink节点就是有出无进,所有输血过多了sink了,通过拓扑排序的定义,sink节点应该是图中排序值最高的节点。
利用DFS进行拓扑排序。
DFS-loop(Graph G):
-- mark allnodes unexplored
--current_label=n (global invariant)
-- for eachvertex v in G:
-- if v not explored
-- DFS(G,v)
DFS(G,v):
-- mark vexplored
-- for everyedge (v,s):
-- if s unexplored:
-- DFS(G,s)
--set f(v)=current_label
--current_label --
用DFS进行拓扑排序的时间复杂度为O(n+m).
还可以利用DFS计算有向图中的SCC。这里主要讲述Kosaraju算法。可以在O(m+n)时间内计算SCC。
算法描述:
注:一个关于SCC的编程题,已经上传到我的微盘上,可以下载http://vdisk.weibo.com/s/9LWVl/1343806060