码迷,mamicode.com
首页 > 编程语言 > 详细

算法导论——拓扑排序

时间:2015-05-24 20:28:47      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!