使用深度优先遍历很容易实现。java
public class DirectedDFS{ private boolean[] marked; //单点可达性 public DirectedDFS(Digraph G,int s){ mark = new boolean[G.V()]; dfs(G,s); } //多点可达性 public DirectedDFS(Digraph G, Iterable<Integer> sources){ mark = new boolean[G.V()]; for(int s : sources) if(!marked[s]) dfs(G,s); } //深度优先遍历算法 private void dfs(Graph G,int v) { marked[v] = true; for(int w: G.adj(v)) if(!marked[w]) dfs(G,w); } public boolean marked(int v){ return marked[v]; } }
在无向图中,这个问题很好解决,等价于连通性问题。无向图中须要线性时间的预处理就能达到常数时间的查询操做。但在有向图中,该问题目前还达不到这样的效率。算法
有向图G的传递闭包是由相同的一组顶点组成的另外一幅有向图,在传递闭包中存在一条从v指向w的边当且仅当G中w是从v可达的。咱们很容易想到经过计算有向图的传递闭包来解决顶点对的可达性问题,但通常来讲,一幅有向图的传递闭包中所含的边比原图中多得多,与其明确计算一幅有向图的传递闭包,不如使用深度优先搜索来实现。数组
public class TransitiveClosure{ private DirectedDFS[] all; //all[]数组中每一个元素都是一个如下标为起始顶点的深度优先遍历逆后序排列 public TransitiveClosure(Digraph G) { all = new DirectedDFS[G.V()]; for(int v = 0; v<G.V();v++) all[v] = new DirectedDFS(G,v); } boolean reachable(int v,int w) { return all[v].marked(w); } }
此方法不适用于实际问题中大型有向图,由于构造函数所须要的空间和V^2成正比,所须要的时间和V(V+E)成正比:共有V个DirectedDFS对象,每一个所需的空间都与V成正比(他们都含有大小为V的marked[]数组并会检查E条边来计算标记)。闭包
本质上,该方法是经过计算G的传递闭包来支持常数时间查询----传递闭包的第v行就是TransitiveClosure类中 DirectedDFS[]数组中第v个元素的marked[]数组。函数
用远小于平方级别的空间支持常数级别的查询的通常解决方案还是一个有待解决的研究问题。spa
有向图强连通份量:在有向图G中,若是两个顶点vi,vj间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。若是有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通份量。.net
Kosaraju算法能够用来计算有向图的强连通份量。code
除了下面代码中标出的两行区别,Kosaraju算法的实现和求无向图的连通性问题的实现几乎彻底相同。Kosaraju算法实现简单但难以理解。在知乎上看到一个对Kosaraju算法的浅显易懂的解释,能够用来帮助理解该算法的原理:https://www.zhihu.com/question/58926821/answer/163724688对象
public class KosarajuSharirSCC { private boolean[] marked; // 已访问过的顶点 private int[] id; // 强连通份量的标识符 private int count; //强连通份量的数量 public KosarajuSharirSCC(Digraph G) { marked = new boolean[G.V()]; id = new int[G.V()]; DepthFirstOrder dfs = new DepthFirstOrder(G.reverse());//区别 for (int v : dfs.reversePost()) {//区别 if (!marked[v]) { dfs(G, v); count++; } } } private void dfs(Digraph G, int v) { marked[v] = true; id[v] = count; for (int w : G.adj(v)) { if (!marked[w]) dfs(G, w); } } public int count() { return count; } public boolean stronglyConnected(int v, int w) { return id[v] == id[w]; } }