1 有向图的数据结构表示
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* 该类用于表示有向图这种数据结构
* @author lhever 2017年2月12日 下午9:23:31
* @version v1.0
*/
public class DirectedGraph
{
private static final String NEWLINE = System.getProperty("line.separator");
private final int V;
private int E;
private List<Integer>[] adj;
private int[] indegree;
/**
* 构造
* @param V 顶点数目
* @author lhever 2017年2月12日 下午7:37:10
* @since v1.0
*/
@SuppressWarnings("unchecked")
public DirectedGraph(int V)
{
if (V <= 0)
{
throw new IllegalArgumentException("有向图中的顶点数必须是一个正整数");
}
this.V = V;
this.E = 0;
indegree = new int[V];
adj = (List<Integer>[]) new ArrayList[V];
for (int v = 0; v < V; v++)
{
adj[v] = new ArrayList<Integer>();
}
}
/**
* 构造方法
* @param G
* 一个已知的有向图对象
* @author lhever 2017年2月12日 下午7:37:47
* @since v1.0
*/
public DirectedGraph(DirectedGraph G)
{
this(G.V());
this.E = G.E();
for (int v = 0; v < V; v++)
{
this.indegree[v] = G.indegree(v);
}
for (int v = 0; v < G.V(); v++)
{
Stack<Integer> reverse = new Stack<Integer>();
for (int w : G.adj[v])
{
reverse.push(w);
}
for (int w : reverse)
{
adj[v].add(w);
}
}
}
/**
* 返回有向图的顶点数目
*
* @return
* @author lhever 2017年2月12日 下午7:35:56
* @since v1.0
*/
public int V()
{
return V;
}
/**
* 返回有向图的边数
*
* @return
* @author lhever 2017年2月12日 下午7:36:17
* @since v1.0
*/
public int E()
{
return E;
}
private void validateVertex(int v)
{
if (v < 0 || v >= V)
{
throw new IndexOutOfBoundsException("顶点编号 " + v + " 必须介于 0 与" + (V - 1) + "之间");
}
}
/**
* 往有向图中添加一条边
*
* @param v
* @param w
* @author lhever 2017年2月12日 下午7:35:28
* @since v1.0
*/
public void addEdge(int v, int w)
{
validateVertex(v);
validateVertex(w);
adj[v].add(w);
indegree[w]++;
E++;
}
/**
* 返回与顶点v相邻的顶点列表
*
* @param v
* @return
* @author lhever 2017年2月12日 下午7:34:45
* @since v1.0
*/
public Iterable<Integer> adj(int v)
{
validateVertex(v);
return adj[v];
}
/**
* 返回顶点v的出度
*
* @param v
* @return
* @author lhever 2017年2月12日 下午7:33:37
* @since v1.0
*/
public int outdegree(int v)
{
validateVertex(v);
return adj[v].size();
}
/**
* 返回顶点v的入度
*
* @param v
* @return
* @author lhever 2017年2月12日 下午7:33:12
* @since v1.0
*/
public int indegree(int v)
{
validateVertex(v);
return indegree[v];
}
/**
* 返回有向图的反向图
*
* @return
* @author lhever 2017年2月12日 下午7:31:01
* @since v1.0
*/
public DirectedGraph reverse()
{
DirectedGraph R = new DirectedGraph(V);
for (int v = 0; v < V; v++)
{
for (int w : adj(v))
{
R.addEdge(w, v);
}
}
return R;
}
public String toString()
{
StringBuilder s = new StringBuilder();
s.append(V + "个顶点, " + E + " 条边 " + NEWLINE);
for (int v = 0; v < V; v++)
{
s.append(String.format("%d: ", v));
for (int w : adj[v])
{
s.append(String.format("%d ", w));
}
s.append(NEWLINE);
}
return s.toString();
}
public static void main(String[] args)
{
DirectedGraph g = new DirectedGraph(13);
g.addEdge(0, 1);
g.addEdge(0, 5);
g.addEdge(2, 3);
g.addEdge(2, 0);
g.addEdge(3, 2);
g.addEdge(3, 5);
g.addEdge(4, 2);
g.addEdge(4, 3);
g.addEdge(5, 4);
g.addEdge(6, 0);
g.addEdge(6, 4);
g.addEdge(6, 9);
g.addEdge(7, 8);
g.addEdge(7, 6);
g.addEdge(8, 9);
g.addEdge(8, 7);
g.addEdge(9, 10);
g.addEdge(9, 11);
g.addEdge(10, 12);
g.addEdge(11, 12);
g.addEdge(11, 4);
g.addEdge(12, 9);
System.out.println(g);
}
}
2 使用字符而串而不是数字命名的有向图的数据结构
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 许多实际问题可以抽象为有向图进行解决,但往往实际问题抽象为图后,其顶点名一般是字符串,该类用于将字符串
* 类型的顶点名与数字命名的顶点进行关联,以便于我们用图的一般方法进行解决问题。
*/
public class SymbolDirectedGraph
{
private Map<String, Integer> st;
private String[] keys;
private DirectedGraph G;
public SymbolDirectedGraph(List<List<String>> lists)
{
st = new HashMap<String, Integer>();
for (List<String> list : lists)
{
for (String key : list)
{
if (!st.containsKey(key))
{
st.put(key, st.size());
}
}
}
keys = new String[st.size()];
for (String name : st.keySet())
{
keys[st.get(name)] = name;
}
G = new DirectedGraph(st.size());
for (List<String> list : lists)
{
int v = st.get(list.get(0));
for (int i = 1; i < list.size(); i++)
{
int w = st.get(list.get(i));
G.addEdge(v, w);
}
}
}
/**
* 判断有向图中是否包含名字为s的顶点
*
* @param s
* @return
* @author lhever 2017年2月12日 下午10:54:00
* @since v1.0
*/
public boolean contains(String s)
{
return st.containsKey(s);
}
/**
* 返回与字符串命名的顶点相关联的数字顶点
*
* @param s
* @return
* @author lhever 2017年2月12日 下午10:54:33
* @since v1.0
*/
public int index(String s)
{
return st.get(s);
}
/**
* 根据数字顶点查找与其关联的字符串顶点名
*
* @param v
* @return
* @author lhever 2017年2月12日 下午10:55:49
* @since v1.0
*/
public String name(int v)
{
return keys[v];
}
public DirectedGraph G()
{
return G;
}
public static void main(String[] args)
{
List<List<String>> lists = new ArrayList<List<String>>();
List<String> li01 = new ArrayList<String>();
li01.add("A");
li01.add("B");
li01.add("C");
li01.add("D");
li01.add("E");
List<String> li02 = new ArrayList<String>();
li02.add("B");
li02.add("F");
List<String> li03 = new ArrayList<String>();
li03.add("E");
li03.add("F");
li03.add("G");
List<String> li04 = new ArrayList<String>();
li04.add("F");
li04.add("G");
lists.add(li01);
lists.add(li02);
lists.add(li03);
lists.add(li04);
SymbolDirectedGraph sg = new SymbolDirectedGraph(lists);
DirectedGraph G = sg.G();
System.out.println(G);
}
}
3 采用深度优先计算有向图中顶点的可达性
import java.util.ArrayList;
import java.util.List;
/**
* 该类用于对有向图进行深度优先访问
* @author lhever 2017年2月12日 下午9:22:33
* @version v1.0
*/
public class DirectedDFS
{
private boolean[] marked;
private int count;
/**
* 利用深度优先算法计算有向图G中从源顶点s可以到达的顶点
*
* @param G
* the 有向图
* @param s
* 起始顶点(源顶点)
*/
public DirectedDFS(DirectedGraph G, int s)
{
marked = new boolean[G.V()];
dfs(G, s);
}
public DirectedDFS(DirectedGraph G, Iterable<Integer> sources)
{
marked = new boolean[G.V()];
for (int v : sources)
{
if (!marked[v]) {
dfs(G, v);
}
}
}
/**
* 有向图的深度优先
*
* @param G
* 有向图
* @param v
* @author lhever 2017年2月12日 下午8:09:30
* @since v1.0
*/
private void dfs(DirectedGraph G, int v)
{
count++;
marked[v] = true;
for (int w : G.adj(v))
{
if (!marked[w]) {
dfs(G, w);
}
}
}
/**
* 判断从源顶点到顶点v之间是否存在一条有向路径
*
* @param v
* @return
* @author lhever 2017年2月12日 下午8:11:07
* @since v1.0
*/
public boolean marked(int v)
{
return marked[v];
}
/**
* 返回从源顶点可达的所有顶点数
*
* @return
* @author lhever 2017年2月12日 下午8:12:53
* @since v1.0
*/
public int count()
{
return count;
}
public static void main(String[] args)
{
DirectedGraph g = new DirectedGraph(13);
g.addEdge(0, 1);
g.addEdge(0, 5);
g.addEdge(2, 3);
g.addEdge(2, 0);
g.addEdge(3, 2);
g.addEdge(3, 5);
g.addEdge(4, 2);
g.addEdge(4, 3);
g.addEdge(5, 4);
g.addEdge(6, 0);
g.addEdge(6, 4);
g.addEdge(6, 9);
g.addEdge(7, 8);
g.addEdge(7, 6);
g.addEdge(8, 9);
g.addEdge(8, 7);
g.addEdge(9, 10);
g.addEdge(9, 11);
g.addEdge(10, 12);
g.addEdge(11, 12);
g.addEdge(11, 4);
g.addEdge(12, 9);
System.out.println(g);
List<Integer> sources = new ArrayList<Integer>();
sources.add(0);
sources.add(3);
sources.add(8);
DirectedDFS dfs = new DirectedDFS(g, sources);
for (int v = 0; v < g.V(); v++)
{
if (dfs.marked(v))
{
System.out.print(v + " ");
}
}
System.out.println();
}
}
4 判断有向图中是否存在环(有向环)
import java.util.Stack;
/**
* 该类用于计算有向图中是否存在有向环
* @author lhever 2017年2月12日 下午9:21:53
* @version v1.0
*/
public class DirectedCycle {
private boolean[] marked;
private int[] edgeTo;
private boolean[] onStack;
private Stack<Integer> cycle;
/**
* 判断有向图中是否存在有向环,如果有,则找出这样其中一条环
* @param G
* @author lhever 2017年2月12日 下午8:35:22
* @since v1.0
*/
public DirectedCycle(DirectedGraph G)
{
marked = new boolean[G.V()];
onStack = new boolean[G.V()];
edgeTo = new int[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v] && cycle == null)
{
dfs(G, v);
}
}
}
/**
* 计算有向图中顶点的拓扑排序,同时找出一条有向环
* @param G
* @param v
* @author lhever 2017年2月12日 下午8:43:59
* @since v1.0
*/
private void dfs(DirectedGraph G, int v)
{
onStack[v] = true;
marked[v] = true;
for (int w : G.adj(v))
{
if (cycle != null)
{
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);
}
cycle.push(w);
cycle.push(v);
assert check();
}
}
onStack[v] = false;
}
/**
* 判断有向图中是否存在有向环
* @return
* @author lhever 2017年2月12日 下午8:54:50
* @since v1.0
*/
public boolean hasCycle()
{
return cycle != null;
}
/**
* 返回有向图中的一条有向环,如果存在的话。
* @return
* @author lhever 2017年2月12日 下午8:55:26
* @since v1.0
*/
public Stack<Integer> cycle()
{
return cycle;
}
/**
* 如果有向图hasCycle()方法表明存在一条有向环,则进行进一步的验证,以便确认是否的确存在这样一条有向环
* @return
* @author lhever 2017年2月12日 下午8:56:28
* @since v1.0
*/
private boolean check()
{
if (hasCycle())
{
int first = -1, last = -1;
for (int v : cycle())
{
if (first == -1) {
first = v;
}
last = v;
}
if (first != last)
{
System.err.printf("有向环开始于顶点 %d 并结束于顶点 %d\n", first, last);
return false;
}
}
return true;
}
public static void main(String[] args)
{
DirectedGraph g = new DirectedGraph(13);
g.addEdge(0, 1);
g.addEdge(0, 5);
g.addEdge(2, 3);
g.addEdge(2, 0);
g.addEdge(3, 2);
g.addEdge(3, 5);
g.addEdge(4, 2);
g.addEdge(4, 3);
g.addEdge(5, 4);
g.addEdge(6, 0);
g.addEdge(6, 4);
g.addEdge(6, 9);
g.addEdge(7, 8);
g.addEdge(7, 6);
g.addEdge(8, 9);
g.addEdge(8, 7);
g.addEdge(9, 10);
g.addEdge(9, 11);
g.addEdge(10, 12);
g.addEdge(11, 12);
g.addEdge(11, 4);
g.addEdge(12, 9);
System.out.println(g);
DirectedCycle dc = new DirectedCycle(g);
if (dc.hasCycle())
{
System.out.print("有向环中的顶点是: ");
Stack<Integer> cycle = dc.cycle();
while(!cycle.isEmpty())
{
Integer v = cycle.pop();
System.out.print(v + " ");
}
System.out.println();
} else
{
System.out.println("该有向图中不存在有向环");
}
System.out.println();
}
}
5 有向图中基于深度优先搜索的顶点排序(前序、后序、逆后序)
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* 该类用于计算有向图中基于深度优先搜索的顶点排序
* @author lhever 2017年2月12日 下午10:04:25
* @version v1.0
*/
public class DepthFirstOrder {
private boolean[] marked;
private int[] pre;
private int[] post;
private List<Integer> preorder;
private List<Integer> postorder;
private Stack<Integer> reversePostOrder;
private int preCounter;
private int postCounter;
/**
* 有向图中基于深度优先搜索的顶点排序(前序、后序、逆后序)
* @param G
* @author lhever 2017年2月12日 下午9:41:35
* @since v1.0
*/
public DepthFirstOrder(DirectedGraph G)
{
pre = new int[G.V()];
post = new int[G.V()];
postorder = new ArrayList<Integer>();
preorder = new ArrayList<Integer>();
reversePostOrder = new Stack<Integer>();
marked = new boolean[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v])
{
dfs(G, v);
}
}
}
private void dfs(DirectedGraph G, int v)
{
marked[v] = true;
pre[v] = preCounter++;
preorder.add(v);
for (int w : G.adj(v))
{
if (!marked[w])
{
dfs(G, w);
}
}
postorder.add(v);
reversePostOrder.push(v);
post[v] = postCounter++;
}
/**
* 返回顶点v在前序排序序列中的序号
* @param v
* @return
* @author lhever 2017年2月12日 下午9:47:24
* @since v1.0
*/
public int pre(int v)
{
return pre[v];
}
/**
* 返回顶点v在后续排序序列中的序号
* @param v
* @return
* @author lhever 2017年2月12日 下午9:47:56
* @since v1.0
*/
public int post(int v)
{
return post[v];
}
/**
* 返回后续排序过的顶点
* @return
* @author lhever 2017年2月12日 下午9:48:29
* @since v1.0
*/
public Iterable<Integer> post()
{
return postorder;
}
/**
* 返回前序排序后的顶点
* @return
* @author lhever 2017年2月12日 下午9:48:51
* @since v1.0
*/
public Iterable<Integer> pre()
{
return preorder;
}
/**
* 返回逆后序排序过的顶点
* @return
* @author lhever 2017年2月12日 下午9:49:57
* @since v1.0
*/
public Iterable<Integer> reversePost()
{
Stack<Integer> reverse = new Stack<Integer>();
for (int v : postorder)
{
reverse.push(v);
}
return reverse;
}
/**
* 另外一种方法返回逆后序排序过的顶点
* @return
* @author lhever 2017年2月12日 下午9:49:57
* @since v1.0
*/
public Iterable<Integer> reversePost2()
{
return reversePostOrder;
}
/**
* 用于验证通过 pre() 或者 post()得到的排序过得顶点的序号是否与分别与通过pre(v)或post(v)方法计算的一致
* @param G
* @return
* @author lhever 2017年2月12日 下午11:05:55
* @since v1.0
*/
private boolean check(DirectedGraph G)
{
int r = 0;
for (int v : post())
{
if (post(v) != r)
{
System.out.println("post(v) 与 post() 计算结果不一致");
return false;
}
r++;
}
r = 0;
for (int v : pre())
{
if (pre(v) != r)
{
System.out.println("pre(v) 与 pre() 计算结果不一致");
return false;
}
r++;
}
return true;
}
public static void main(String[] args)
{
DirectedGraph g = new DirectedGraph(13);
g.addEdge(0, 1);
g.addEdge(0, 5);
g.addEdge(2, 3);
g.addEdge(2, 0);
g.addEdge(3, 2);
g.addEdge(3, 5);
g.addEdge(4, 2);
g.addEdge(4, 3);
g.addEdge(5, 4);
g.addEdge(6, 0);
g.addEdge(6, 4);
g.addEdge(6, 9);
g.addEdge(7, 8);
g.addEdge(7, 6);
g.addEdge(8, 9);
g.addEdge(8, 7);
g.addEdge(9, 10);
g.addEdge(9, 11);
g.addEdge(10, 12);
g.addEdge(11, 12);
g.addEdge(11, 4);
g.addEdge(12, 9);
System.out.println(g);
DepthFirstOrder dfs = new DepthFirstOrder(g);
System.out.println( "有向图合法性校验结果是:" + dfs.check(g));
System.out.println(" 顶点v 前序排序序号 后续排序序号");
for (int v = 0; v < g.V(); v++)
{
System.out.println(String.format("%4d %4d %4d\n", v, dfs.pre(v), dfs.post(v)));
}
System.out.print("前序排序: ");
for (int v : dfs.pre())
{
System.out.print(v + " ");
}
System.out.println();
System.out.print("后序排序: ");
for (int v : dfs.post())
{
System.out.print(v + " ");
}
System.out.println();
System.out.print("逆后续排序:");
for (int v : dfs.reversePost())
{
System.out.print(v + " ");
}
System.out.println();
}
}
6 有向图中顶点的拓扑排序
import java.util.ArrayList;
import java.util.List;
/**
* 该类用于计算有向图的拓扑排序
* @author lhever 2017年2月12日 下午10:22:47
* @version v1.0
*/
public class Topological {
private Iterable<Integer> order;
private int[] rank;
/**
* 判断有向图是否具体拓扑排序,如果有,则找出其拓扑排序
* @param G
* @author lhever 2017年2月12日 下午10:23:45
* @since v1.0
*/
public Topological(DirectedGraph G)
{
DirectedCycle dc = new DirectedCycle(G);
if (!dc.hasCycle())
{
DepthFirstOrder dfs = new DepthFirstOrder(G);
order = dfs.reversePost();
rank = new int[G.V()];
int i = 0;
for (int v : order)
{
rank[v] = i++;
}
}
}
/**
* 返回有向图的拓扑排序序列
* @return
* @author lhever 2017年2月12日 下午10:30:30
* @since v1.0
*/
public Iterable<Integer> order()
{
return order;
}
/**
* 判断有向图是否具有拓扑排序
* @return
* @author lhever 2017年2月12日 下午10:27:03
* @since v1.0
*/
public boolean hasOrder()
{
return order != null;
}
/**
* 返回顶点v在拓扑排序的序列中的序号
* @param v
* @return
* @author lhever 2017年2月12日 下午10:29:31
* @since v1.0
*/
public int rank(int v)
{
validateVertex(v);
if (hasOrder())
{
return rank[v];
} else
{
return -1;
}
}
private void validateVertex(int v)
{
int V = rank.length;
if (v < 0 || v >= V)
{
throw new IndexOutOfBoundsException("顶点 " + v + " 没有介于 0 和 " + (V - 1) + "之间");
}
}
public static void main(String[] args)
{
List<List<String>> lists = new ArrayList<List<String>>();
List<String> li01 = new ArrayList<String>();
li01.add("A");
li01.add("B");
li01.add("C");
li01.add("D");
li01.add("E");
List<String> li02 = new ArrayList<String>();
li02.add("B");
li02.add("F");
List<String> li03 = new ArrayList<String>();
li03.add("E");
li03.add("F");
li03.add("G");
List<String> li04 = new ArrayList<String>();
li04.add("F");
li04.add("G");
lists.add(li01);
lists.add(li02);
lists.add(li03);
lists.add(li04);
SymbolDirectedGraph sg = new SymbolDirectedGraph(lists);
Topological topological = new Topological(sg.G());
for (int v : topological.order())
{
System.out.print(sg.name(v) + " " );
}
}
}
7 计算有向图中强连通分量的Kosaraju算法
import java.util.ArrayList;
import java.util.List;
/**
* 该类其实是用于计算有向图的强连通分量的KosarajuSCC算法
*
* @author lhever 2017年2月13日 下午11:42:54
* @version v1.0
*/
public class KosarajuSCC
{
private boolean[] marked;
private int[] id;
private int count;
/**
* 计算有向图的强连通分量
*
* @param G
* @author lhever 2017年2月12日 下午11:49:37
* @since v1.0
*/
public KosarajuSCC(DirectedGraph G)
{
DepthFirstOrder dfs = new DepthFirstOrder(G.reverse());
marked = new boolean[G.V()];
id = new int[G.V()];
for (int v : dfs.reversePost())
{
if (!marked[v])
{
dfs(G, v);
count++;
}
}
assert check(G);
}
private void dfs(DirectedGraph G, int v)
{
marked[v] = true;
id[v] = count;
for (int w : G.adj(v))
{
if (!marked[w])
{
dfs(G, w);
}
}
}
/**
* 返回强连通分量的数量
*
* @return
* @author lhever 2017年2月13日 下午11:47:21
* @since v1.0
*/
public int count()
{
return count;
}
/**
* 判断顶点v和顶点w是否在同一个强连通分量里面
*
* @param v
* @param w
* @return
* @author lhever 2017年2月13日 下午11:47:52
* @since v1.0
*/
public boolean stronglyConnected(int v, int w)
{
return id[v] == id[w];
}
/**
* 返回包含顶点v的强连通分量的id
*
* @param v
* @return
* @author lhever 2017年2月13日 下午11:49:07
* @since v1.0
*/
public int id(int v)
{
return id[v];
}
private boolean check(DirectedGraph G)
{
TransitiveClosure tc = new TransitiveClosure(G);
for (int v = 0; v < G.V(); v++)
{
for (int w = 0; w < G.V(); w++)
{
if (stronglyConnected(v, w) != (tc.reachable(v, w) && tc.reachable(w, v)))
{
return false;
}
}
}
return true;
}
public static void main(String[] args)
{
DirectedGraph g = new DirectedGraph(13);
g.addEdge(0, 1);
g.addEdge(0, 5);
g.addEdge(2, 3);
g.addEdge(2, 0);
g.addEdge(3, 2);
g.addEdge(3, 5);
g.addEdge(4, 2);
g.addEdge(4, 3);
g.addEdge(5, 4);
g.addEdge(6, 0);
g.addEdge(6, 4);
g.addEdge(6, 9);
g.addEdge(7, 8);
g.addEdge(7, 6);
g.addEdge(8, 9);
g.addEdge(8, 7);
g.addEdge(9, 10);
g.addEdge(9, 11);
g.addEdge(10, 12);
g.addEdge(11, 12);
g.addEdge(11, 4);
g.addEdge(12, 9);
System.out.println(g);
KosarajuSCC scc = new KosarajuSCC(g);
int M = scc.count();
System.out.println("本系统包含" + M + "个连通分量");
@SuppressWarnings("unchecked")
List<Integer>[] components = new ArrayList[M];
for (int i = 0; i < M; i++)
{
components[i] = new ArrayList<Integer>();
}
for (int v = 0; v < g.V(); v++)
{
components[scc.id(v)].add(v);
}
for (int i = 0; i < M; i++)
{
for (int v : components[i])
{
System.out.print(v + " ");
}
System.out.println();
}
}
}
public class TransitiveClosure
{
private DirectedDFS[] tc;
public TransitiveClosure(DirectedGraph G)
{
tc = new DirectedDFS[G.V()];
for (int v = 0; v < G.V(); v++)
{
tc[v] = new DirectedDFS(G, v);
}
}
/**
* 判断在有向图中是否存在一条从顶点v到定点w的有向路径
*
* @param v
* @param w
* @return
* @author lhever 2017年2月13日 下午11:56:35
* @since v1.0
*/
public boolean reachable(int v, int w)
{
return tc[v].marked(w);
}
public static void main(String[] args)
{
DirectedGraph g = new DirectedGraph(13);
g.addEdge(0, 1);
g.addEdge(0, 5);
g.addEdge(2, 3);
g.addEdge(2, 0);
g.addEdge(3, 2);
g.addEdge(3, 5);
g.addEdge(4, 2);
g.addEdge(4, 3);
g.addEdge(5, 4);
g.addEdge(6, 0);
g.addEdge(6, 4);
g.addEdge(6, 9);
g.addEdge(7, 8);
g.addEdge(7, 6);
g.addEdge(8, 9);
g.addEdge(8, 7);
g.addEdge(9, 10);
g.addEdge(9, 11);
g.addEdge(10, 12);
g.addEdge(11, 12);
g.addEdge(11, 4);
g.addEdge(12, 9);
System.out.println(g);
TransitiveClosure tc = new TransitiveClosure(g);
System.out.print(" ");
for (int v = 0; v < g.V(); v++)
{
System.out.print(String.format("%3d", v));
}
System.out.println();
System.out.println("--------------------------------------------");
for (int v = 0; v < g.V(); v++)
{
System.out.print(String.format("%3d: ", v));
for (int w = 0; w < g.V(); w++)
{
if (tc.reachable(v, w))
{
System.out.print(" T");
} else
{
System.out.print(" ");
}
}
System.out.println();
}
}
}