package org.loda.graph;
import org.loda.structure.Stack;
import org.loda.util.In;
/**
*
* @ClassName: Topological
* @Description: 拓扑排序是所有节点dfs的逆后序,也就是每个节点任务完成的时间的逆序排序
* @author minjun
* @date 2015年5月24日 下午7:17:53
*
*/
public class Topological {
/**
* 由于拓扑排序是df获取所有节点的逆后序排序
* 这里利用Stack后序存储元素,那么获取出来就是反向(逆)后序排列的拓顺序
*/
private Stack<Integer> topOrder;
//是否访问过该元素
private boolean[] visited;
public Topological(Digraph g){
//由于拓扑排序要求有向图是无环的,所以在进行排序之前先检测一下该有向图是否有环
CheckDigraphCycle c=new CheckDigraphCycle(g);
if(c.hasCycle()){
System.out.println("该有向图有环,不能进行拓扑排序");
}else{
int v=g.v();
topOrder=new Stack<Integer>();
visited=new boolean[v];
for(int i=0;i<v;i++){
if(!visited[i]){
dfs(i,g);
}
}
}
}
private void dfs(int v, Digraph g) {
visited[v]=true;
for(int w:g.adj(v)){
if(!visited[w]){
dfs(w, g);
}
}
//入栈
topOrder.push(v);
}
/**
*
* @Title: order
* @Description: 获取拓扑顺序
* @param @return 设定文件
* @return Iterable<Integer> 返回类型
* @throws
*/
public Iterable<Integer> order(){
return topOrder;
}
public static void main(String[] args) {
Digraph d=new Digraph(new In("F:\\算法\\attach\\tinyDAG.txt"));
Topological t=new Topological(d);
Iterable<Integer> it=t.order();
if(it!=null){
System.out.println("拓扑顺序为:");
//拓扑顺序
for(int i:it){
System.out.print(i+"->");
}
}
}
}
package org.loda.graph;
import org.loda.structure.Stack;
import org.loda.util.In;
/**
*
* @ClassName: CheckDigraphCycle
* @Description: 检测有向图是否有环
* @author minjun
* @date 2015年5月24日 下午5:23:03
*
*/
public class CheckDigraphCycle {
//检查是否已经入栈,这是检测是否有环的依据
private boolean[] inStack;
//如果有环,那么找出一条环cycle,否则返回null
private Stack<Integer> cycle;
//是否访问过
private boolean[] visited;
//这条路径的上一个元素
private int[] prev;
public CheckDigraphCycle(Digraph g){
int v=g.v();
visited=new boolean[v];
inStack=new boolean[v];
prev=new int[v];
for(int i=0;i<v;i++){
prev[i]=-1;
}
for(int i=0;i<v;i++){
if(!visited[i]){
dfs(i,g);
}
}
}
private void dfs(int v, Digraph g) {
visited[v]=true;
//入栈
inStack[v]=true;
for(int w:g.adj(v)){
//如果有环,直接返回,这样可以减少不必要的计算步骤
if(hasCycle()) return;
//如果没有访问过的元素,就走正常流程,继续访问它
//如果访问过该元素,并且该元素已经入栈,还未出栈,表示该元素之前被访问过了,并且一路访问回来还是继续访问这个元素,说明这是一个环
//如果访问过该元素,并且钙元素已经出栈,则不进行任何操作
if(!visited[w]){
prev[w]=v;
dfs(w, g);
}else if(inStack[w]){
//找到那个有向环
cycle=new Stack<Integer>();
cycle.push(w);
//开始找v的prev节点,并一直沿着倒序找prev节点,一直找到w
for(int i=v;i!=w;i=prev[i]){
cycle.push(i);
}
cycle.push(w);
}
}
//出栈
inStack[v]=false;
}
/**
*
* @Title: hasCycle
* @Description: 查看是否有环
* @param @return 设定文件
* @return boolean 返回类型
* @throws
*/
public boolean hasCycle(){
return cycle!=null;
}
/**
*
* @Title: cycle
* @Description: 找到这个环,如果没有,返回null
* @param @return 设定文件
* @return Iterable<Integer> 返回类型
* @throws
*/
public Iterable<Integer> cycle(){
return cycle;
}
public static void main(String[] args) {
Digraph d=new Digraph(new In("F:\\算法\\attach\\tinyDG.txt"));
CheckDigraphCycle c=new CheckDigraphCycle(d);
if(c.hasCycle()){
System.out.println("有环,列出其中一个环:");
for(int i:c.cycle){
System.out.print(i+"->");
}
}else{
System.out.println("没环");
}
}
}
输出结果是:
拓扑顺序为:
8->7->2->3->0->6->9->10->11->12->1->5->4->
其中In对象引入的IO流文件的数据为:
13
15
2 3
0 6
0 1
2 0
11 12
9 12
9 10
9 11
3 5
8 7
5 4
0 5
6 4
6 9
7 6
第一行为顶点数13,第二行是要添加的边的个数15,后面就是添加的所有边
原文:http://my.oschina.net/u/1378920/blog/419291