有向图由一组顶点和一组有方向的边组成
有向图的API
public class | Digraph | - |
---|---|---|
Digraph(int v) | ||
Digraph(In in) | ||
int | V() | 顶点数 |
int | E() | 边数 |
void | addEdge(int v, int w) | 添加边v-w |
Iterable | adj(int v) | 和v相邻的所有顶点 |
Digraph | reverse | 该图的反向图 |
String | toString() |
有向环
寻找有向环
public class DirectedCycle {
private boolean marked[];
private int[] edgeTo;
private Stack<Integer> cycle; //有向环中的所有顶点(如果存在)
private boolean[] onStack; //递归调用的栈上的所有顶点
public DirectedCycle(Digraph g){
onStack = new boolean[g.V()];
edgeTo = new int[g.V()];
marked = new boolean[g.V()];
for(int v=0; v<g.V(); v++)
if(!marked[v]) dfs(g, v);
}
public void dfs(Digraph g, int v){
onStack[v] = true; //调用v时设为true,调用结束设为false
marked[v] = true;
for(int w : g.adj(v)){
if(this.hasCycle()) return;
else if(!marked[w]){
edgeTo[w] = v;
dfs(g, w);
}else if(onStack[w]){
cycle = new Stack<Integer>();
for(int x=v; x!=w; x=edgeTo[x])
cycle.push(x); //4 3 3为栈底
cycle.push(w); // 5 4 3
cycle.push(v); // 3 5 4 3
}
onStack[v] = false;
}
}
public boolean hasCycle(){
return cycle!=null;
}
public Iterable<Integer> cycle(){
return cycle;
}
}
顶点排序(有向无环图)
- 前序: 在递归调用之前将顶点加入队列
- 后序: 在递归调用之后将顶点加入队列
- 逆后序: 在递归调用之后将顶点压入栈
拓扑排序:之前(指向此任务的任务)任务已完成
一幅有向无环图的拓扑顺序即为所有顶点的逆后序排列
DepthFirstOrder(Graph g) - pre 前序 post 后序 reversePost 逆后序
强连通性
如果一幅有向图中的任意两个顶点都是强连通的(互相可达),则称这幅有向图是强连通的.
计算强连通分量的Kosaraju算法
- 使用深度优先搜索查找给定有向图G1的反向图G2,
- 得到G2所有顶点的逆后序
- 使用深度优先搜索处理有向图G1(按照第二步所得顺序)
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;
}
}