有向图中的强连通性
定义:如果两个顶点v和w是互相可达的,则称它们为强连通的。也就是说,既存在一条从v到w的有向路径,也存在一条从w到v的有向路径。如果从一幅有向图中的任意两个顶点都是强连通的,则称这幅有向图也是强连通的。
两个顶点是强连通的当且仅当它们都在一个普通的有向环中。
强连通分量
和无向图中的连通性一样,有向图中的强连通性也是一种顶点之间的等价关系,因为它有着以下性质:
- 自反性:任意顶点v和自己都是强连通的。
- 对称性:如果v和w是强连通的,那么w和v也是强连通的。
- 传递性:如果v和w是强连通的且w和x也是强连通的,那么v和x也是强连通的。
作为一种等价关系,强连通性将所有顶点分为了一些等价类,每个等价类都是由相互均为强连通的顶点的最大子集组成的,我们将这些子集称为强连通分量。
一个含有V个顶点的有向图含有1~V个强连通分量,一个强连通图只含有一个强连通分量,而一个有向无环图中则含有V个强连通分量。
Kosaraju算法
命题:使用深度优先搜索查找给定有向图的反向图,根据由此得到的所有顶点的逆后序再次用深度优先搜索处理有向图,其构造函数中的每一次递归调用所标记的顶点都在同一个强连通分量之中。
代码
package section4_2;
public class KosarajuSCC {
private boolean[] marked;
private int[] id;
private int count;
public KosarajuSCC(Digraph G) {
marked = new boolean[G.V()];
id = new int[G.V()];
DepthFirstOrder order = new DepthFirstOrder(G.reverse());
for (int s : order.reversePost()) {
if (!marked[s]) {
dfs(G,s);
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 boolean stronglyConnected(int v, int w) {
return id[v] == id[w];
}
public int id(int v) {
return id[v];
}
public int count() {
return count;
}
public static void main(String[] args) {
int[][] data = {
{4,2},
{2,3},
{3,2},
{6,0},
{0,1},
{2,0},
{11,12},
{12,9},
{9,10},
{9,11},
{8,9},
{10,12},
{11,4},
{4,3},
{3,5},
{7,8},
{8,7},
{5,4},
{0,5},
{6,4},
{6,9},
{7,6}
};
int vn = 13;
int en = 22;
Digraph digraph = new Digraph(vn,en,data);
KosarajuSCC scc = new KosarajuSCC(digraph);
System.out.println(scc.count());
System.out.println(scc.id(1));
System.out.println(scc.id(2));
System.out.println(scc.id(9));
System.out.println(scc.id(6));
System.out.println(scc.id(8));
}
}