一.概述
拓扑排序:
给定一副有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素,此时就可以明确的表示出每个顶点的优先级。 最终得到一个有序序列。
如果要使用拓扑排序解决优先级问题,需要先保证有向图中没有环!
API:
检测有向环的过程:
在API中添加了onStack[] 布尔数组,索引为图的顶点,当我们深度优先搜索时:
- 在如果当前顶点正在搜索,则把对应的onStack数组中的值改为true,标识进栈;
- 如果当前顶点搜索完毕,则把对应的onStack数组中的值改为false,标识出栈;
- 如果即将要搜索某个顶点,但该顶点已经在栈中,则图中有环;
关于onStack ! ※
当顶点已经被搜索过即marked[v]=true时,常规dfs到此就是递归结束了,而在这里将进入判断,如果之前在inStack中为true,代表该顶点已经进入了搜索路径,再遇到即为存在环!
遍历完后,所有经过dfs遍历的顶点要onStack弹出栈!因为不同的搜索路径可能会有重复的顶点!要将onStack里的元素还原为false,再从新的顶点出发进行搜索。
从1出发搜索到3, 0出发也会搜索到3! onStack要出栈!
二.实现
public class DirectCycle {//检测图中是否有环的存在
private boolean[] marked; //索引代表顶点,值代表当前顶点是否被搜索
private boolean hasCycle;
private boolean[] onStack; //使用栈的思想,记录顶点是否处于正在搜索的有向路径上
public DirectCycle(Digraph g) {
this.marked=new boolean[g.V()];
this.hasCycle=false;
this.onStack=new boolean[g.V()];
//找到图中每一个顶点!让每一个顶点都作为入口去用dfs搜索!
for (int i = 0; i < g.V(); i++) {
if (!marked[i]) { //如果没有搜索过则使用dfs进行搜索。搜索过就不用dfs了 !
dfs(g, i);
}
}
}
private void dfs(Digraph g,int v){ //还是用dfs深度优先来搜索
marked[v]=true; //标记v为已搜索
onStack[v]=true; //让当前顶点进栈
//dfs搜索, 并检查是否存在环!
for (Integer k : g.adj(v)) { //遍历当前顶点的邻接表
if (!marked[k]) { //判断视为递归出口
dfs(g, k); //没有被搜索过则继续递归dfs
} else {
// marked和ontack看似一样,但是,不同的搜索路径可能会有重复的顶点,所以必须用onStack来记录每一次的路径上的点然后顶点出栈将onStack清空!
//当邻接表中的顶点已经被搜索过时,走到这一步, 普通dfs中会到此就已经递归结束了。
//判断当前顶点是否在栈中,如果是则表示该顶点处于正在搜索的状态,即存在环;否则出栈
if (onStack[k]) { //如果栈中存在,则判断为存在环
hasCycle = true;
return; //有环则直接结束。
}
}
}
// 让onStack还原为全false
onStack[v]=false;
}
public boolean hasCycle(){
return hasCycle;
}
private boolean[] onStack(){//索引代表顶点,使用栈的思想,记录当前顶点有没有处于搜索路径上!
return onStack;
}
}