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,后面就是添加的所有边
时间: 2024-10-20 05:30:27