算法导论——拓扑排序

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

算法导论——拓扑排序的相关文章

[算法导论]拓扑排序 @ Python

class Graph: def __init__(self): self.V = [] class Vertex: def __init__(self, x): self.key = x self.color = 'white' self.d = 10000 self.f = 10000 self.pi = None self.adj = [] self.next = None class Solution: def Dfs(self, G): for u in G.V: u.color =

算法8-10:最短路径算法之拓扑排序

该算法的基本思想就是按照拓扑排序的顺序依次将每个顶点加入到最短路径树中,每次加入时将该顶点延伸出的所有顶点进行"放松"操作.这种算法的复杂度是E+V. 代码 这种算法的代码比Dijkstra还要简单,代码如下: public class TopologySP extends SP { public TopologySP(EdgeWeightedDigraph G, int s) { super(G, s); // 将所有顶点到原点的距离设为无穷大 // 注意:下面这段代码不要遗漏 fo

有向图算法之拓扑排序

拓扑排序的意思: 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前.通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列. 一个较大的工程往往被划分成许多子工程,我们把这些子工程称作活动(activity).在整个工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的

算法导论--------------计数排序and基数排序

计数排序假设n个输入元素中的每一个都介于0和k之间的整数,k为n个数中最大的元素.当k=O(n)时,计数排序的运行时间为θ(n).计数排序的基本思想是:对n个输入元素中每一个元素x,统计出小于等于x的元素个数,根据x的个数可以确定x在输出数组中的最终位置.此过程需要引入两个辅助存放空间,存放结果的B[1...n],用于确定每个元素个数的数组C[0...k].算法的具体步骤如下: (1)根据输入数组A中元素的值确定k的值,并初始化C[1....k]= 0: (2)遍历输入数组A中的元素,确定每个元

算法导论------------桶排序算法之研究

举个来说明桶排序的过程,假设现在有A={0.78,0.17,0.39,0.26,0.72,0.94,0.21,0.12,0.23,0.68},桶排序如下所示: 研究过计数排序我们知道了----计数排序是假设输入是由一个小范围内的整数构成,而桶排序则假设输入由一个随机过程产生的,该过程将元素均匀而独立地分布在区间[0,1)上.当桶排序的输入符合均匀分布时,即可以线性期望时间运行.桶排序的思想是:把区间[0,1)划分成n个相同大小的子区间,成为桶(bucket),然后将n个输入数分布到各个桶中去,对

算法导论 计数排序

计数排序的关键就在于如何处理每个元素的最终位置.在计数排序中,我们可以通过维护一个数组C[i]来记录键值为i的元素所属的位置.每次输入一个A[i],首先记录每个A[i]出现的次数C[i],然后从前向后C[i]=C[i-1]+C[i],这样可以得出值为i所在排序后新数组中的最后一个重复数的位置.计数排序的一个显然问题就是C[]数组的大小确定的问题.下面贴上我自己理解写出的代码. #include<iostream> using namespace std; int main() { int n,

每日一算法之拓扑排序

伪代码: TopLogical(G) call DFS to compute finishtime as each vertex finished , insert it onto the front of a linked list return the linked list of vertices 实现如下: #include <iostream> #include <vector> #include <memory> using namespace std; v

有向无环图的应用—AOV网 和 拓扑排序

有向无环图:无环的有向图,简称 DAG (Directed Acycline Graph) 图. 一个有向图的生成树是一个有向树,一个非连通有向图的若干强连通分量生成若干有向树,这些有向数形成生成森林. 在工程计划和管理方面的应用 除最简单的情况之外,几乎所有的工程都可分为若干个称作“活动”的子工程,并且这些子工程之间通常受着一定条件的约束,例如:其中某些子工程必须在另一些子工 程完成之后才能开始.对整个工程和系统,人们关心的是两方面的问题: 一是工程能否顺利进行,即工程流程是否“合理”: 二是

拓扑排序的原理及事实上现

本文将从下面几个方面介绍拓扑排序: 拓扑排序的定义和前置条件 和离散数学中偏序/全序概念的联系 典型实现算法 Kahn算法 基于DFS的算法 解的唯一性问题 实际样例 取材自下面材料: http://en.wikipedia.org/wiki/Topological_sorting http://en.wikipedia.org/wiki/Hamiltonian_path 定义和前置条件: 定义:将有向图中的顶点以线性方式进行排序.即对于不论什么连接自顶点u到顶点v的有向边uv,在最后的排序结果