1.定义
在现实生活中,我们经常会同一时间接到很多任务去完成,但是这些任务的完成是有先后次序的。以我们学习java 学科为例,我们需要学习很多知识,但是这些知识在学习的过程中是需要按照先后次序来完成的。从java基础,到 jsp/servlet,到ssm,到springboot等是个循序渐进且有依赖的过程。在学习jsp前要首先掌握java基础和html基 础,学习ssm框架前要掌握jsp/servlet之类才行。 
为了简化问题,我们使用整数为顶点编号的标准模型来表示这个案例: 
此时如果某个同学要学习这些课程,就需要指定出一个学习的方案,我们只需要对图中的顶点进行排序,让它转换 为一个线性序列,就可以解决问题,这时就需要用到一种叫拓扑排序的算法。
拓扑排序:
给定一副有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素,此时就可以明 确的表示出每个顶点的优先级。下列是一副拓扑排序后的示意图:
2.检测有向图中的环
如果学习x课程前必须先学习y课程,学习y课程前必须先学习z课程,学习z课程前必须先学习x课程,那么一定是有 问题了,我们就没有办法学习了,因为这三个条件没有办法同时满足。其实这三门课程x、y、z的条件组成了一个环: 
实现:
在API中添加了onStack[] 布尔数组,索引为图的顶点,当我们深度搜索时:
1. 在如果当前顶点正在搜索,则把对应的onStack数组中的值改为true,标识进栈;
2. 如果当前顶点搜索完毕,则把对应的onStack数组中的值改为false,标识出栈;
3. 如果即将要搜索某个顶点,但该顶点已经在栈中,则图中有环;

代码实现:
package 图;public class DirectedCycle {//索引代表顶点,值表示当前顶点是否已经被搜索private boolean[] marked;//记录图中是否有环private boolean hasCycle;//索引代表顶点,使用栈的思想,记录当前顶点有没有已经处于正在搜索的有向路径上private boolean[] onStack;//创建一个检测环对象,检测图G中是否有环public DirectedCycle(Digraph G) {//创建一个和图的顶点数一样大小的marked数组marked = new boolean[G.V()];//创建一个和图的顶点数一样大小的onStack数组onStack = new boolean[G.V()];//默认没有环this.hasCycle = false;//遍历搜索图中的每一个顶点for (int v = 0; v < G.V(); v++) {//如果当前顶点没有搜索过,则搜索if (!marked[v]) {dfs(G, v);}}}//基于深度优先搜索,检测图G中是否有环private void dfs(Digraph G, int v) {//把当前顶点标记为已搜索marked[v] = true;//让当前顶点进栈onStack[v] = true;//遍历v顶点的邻接表,得到每一个顶点wfor (Integer w : G.adj(v)) {//如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点if (!marked[w]) {dfs(G, w);}//如果顶点w已经被搜索过,则查看顶点w是否在栈中,如果在,则证明图中有环,修改hasCycle标记,结束循环if (onStack[w]) {hasCycle = true;return;}}//当前顶点已经搜索完毕,让当前顶点出栈onStack[v] = false;}//判断w顶点与s顶点是否相通public boolean hasCycle() {return hasCycle;}}
3. 基于深度优先的顶点排序
实现:
在API的设计中,我们添加了一个栈reversePost用来存储顶点,当我们深度搜索图时,每搜索完毕一个顶点,把该 顶点放入到reversePost中,这样就可以实现顶点排序。 

代码实现:
package 图;import 线性表.Stack;public class DepthFirstOrder {//索引代表顶点,值表示当前顶点是否已经被搜索private boolean[] marked;//使用栈,存储顶点序列private Stack<Integer> reversePost;//创建一个检测环对象,检测图G中是否有环public DepthFirstOrder(Digraph G) {//创建一个和图的顶点数一样大小的marked数组marked = new boolean[G.V()];reversePost = new Stack<Integer>();//遍历搜索图中的每一个顶点for (int v = 0; v < G.V(); v++) {//如果当前顶点没有搜索过,则搜索if (!marked[v]) {dfs(G, v);}}}//基于深度优先搜索,检测图G中是否有环private void dfs(Digraph G, int v) {//把当前顶点标记为已搜索marked[v] = true;//遍历v顶点的邻接表,得到每一个顶点wfor (Integer w : G.adj(v)) {//如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点if (!marked[w]) {dfs(G, w);}}//当前顶点已经搜索完毕,让当前顶点入栈reversePost.push(v);}//获取顶点线性序列public Stack<Integer> reversePost() {return reversePost;}}
4. 拓扑排序实现
前面已经实现了环的检测以及顶点排序,那么拓扑排序就很简单了,基于一幅图,先检测有没有环,如果没有环, 则调用顶点排序即可。
代码实现:
package 图;import 线性表.Stack;public class TopoLogical {//顶点的拓扑排序private Stack<Integer> order;//构造拓扑排序对象public TopoLogical(Digraph G) {//创建检测环对象,检测图G中是否有环DirectedCycle dCycle = new DirectedCycle(G);if (!dCycle.hasCycle()) {//如果没有环,创建顶点排序对象,进行顶点排序DepthFirstOrder depthFirstOrder = new DepthFirstOrder(G);order = depthFirstOrder.reversePost();}}//判断图G是否有环private boolean isCycle() {return order == null;}//获取拓扑排序的所有顶点public Stack<Integer> order() {return order;}}
