算法导论——拓扑排序

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->




转载于:https://my.oschina.net/u/1378920/blog/419291

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值