package org.loda.graph;
import org.loda.util.In;
/**
*
* @ClassName: Topological
* @Description: 拓扑排序是所有节点dfs的逆后序,也就是每个节点任务完成的时间的逆序排序
* @author minjun
* @date 2015年5月24日 下午7:17:53
*
*/
public class Topological {
/**
* 由于拓扑排序是df获取所有节点的逆后序排序
*/
private Iterable<Integer> topOrder;
public Topological(Digraph g){
//由于拓扑排序要求有向图是无环的,所以在进行排序之前先检测一下该有向图是否有环
CheckDigraphCycle c=new CheckDigraphCycle(g);
if(c.hasCycle()){
System.out.println("该有向图有环,不能进行拓扑排序");
}else{
//获取逆后序
DeptFirstOrder d=new DeptFirstOrder(g);
topOrder=d.reversePost();
}
}
/**
*
* @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("没环");
}
}
}
package org.loda.graph;
import org.loda.structure.Queue;
import org.loda.structure.Stack;
/**
*
* @ClassName: DeptFirstOrder
* @Description: 获取顺序、逆序、逆后序
* @author minjun
* @date 2015年5月25日 上午12:08:03
*
*/
public class DeptFirstOrder {
//前序
private Queue<Integer> prev;
//逆序
private Queue<Integer> post;
//逆后序
private Stack<Integer> reversePost;
//是否访问过该元素
private boolean[] visited;
public DeptFirstOrder(Digraph g){
int v=g.v();
prev=new Queue<Integer>();
post=new Queue<Integer>();
reversePost=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;
//入列
prev.enqueue(v);
for(int w:g.adj(v)){
if(!visited[w]){
dfs(w, g);
}
}
//入列
post.enqueue(v);
//入栈
reversePost.push(v);
}
public Iterable<Integer> prev(){
return prev;
}
public Iterable<Integer> post(){
return post;
}
public Iterable<Integer> reversePost(){
return reversePost;
}
}
其中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,后面就是添加的所有边
------------------------------------------------------------------------------
输出内容为:
拓扑顺序为:
8->7->2->3->0->6->9->10->11->12->1->5->4->