优先级限制下的调度问题:给定一组需要完成的任务,以及一组关于任务完成的先后次序的优先级限制。在满足限制条件的前提下应该如何安排并完成所有任务?
拓扑排序:给定一幅有向图,将所有顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素(或者说明无法做到这一点)。
优先级限制下不应该存在有向环,一个优先级限制的问题如果存在有向环,那么这个问题 肯定是无解的。
先来解决有向环检测问题:
采用深度优先遍历来解决这个问题:用一个栈表示“当前”正在遍历的有向路径上的顶点。一旦找到一条有向边v->w,并且w已经存在于栈中,那么就找到了一个环;如果没有找到这条边,那么就是无环图。
实现:
public class DirectedCycle {
private boolean[] marked;
private int [] edgeTo;
private Stack<Integer> cycle;//有向环中所有顶点
private boolean[] onStack;//递归调用栈中所有顶点
public DirectedCycle(Digraph G) {
//略
}
//修改过的深度优先遍历
public void dfs(Digraph G,int v) {
onStack[v] = true;
marked[v] = true;
for(int w: G.adj(v)){
if(this.hasCycle()) return; //检测出有环,算法停止
else if(!marked[v]) {
edgeTo[w] = v; dfs(G,w);
}
else if(onStack[w]) { //如果为真,则说明形成了环,把环路经保存下来
cycle = new Stack<Integer>();
for(int x = v;x!= w;x=edgeTo[x])
cycle.push(x);
cycle.push(w);
cycle.push(v);
}
}
}
public boolean hasCycle() { return cycle != null; }
public Iterable<Integer> cycle(){ return cycle; }
}
其实,将有向无环图的深度优先遍历的路径记录下来就是一种拓扑排序!遍历的顺序取决于数据结构的性质以及是在递归调用之前还是之后保存。一般有三种排列排序:
基于深度优先搜索的顶点排序:
public class DepthFirstOrder{
private boolean[] marked;
private Queue<Integer> pre;//所有顶点的前序排列
private Queue<Integer> post;//所有顶点的后序排列
private Stack<Integer> reversePost;//所有顶点的逆后序排列
//构造器
public DepthFirstOrder(Digraph G){
pre = new Queue<Integer>();
post = new Queue<Integer>();
revercePost = new Stack<Integer>();
marked = new boolean[G.V()];
for(int v = 0; v < G.V(); v++)
if(!marked[v]) dfs(G,v);
}
//深度优先遍历
private void dfs(Digraph G,int v) {
pre.enqueue(v);
marked[v] = true;
for(int w:G.adj(v))
if(!marked[w])
dfs(G,w);
post.enqueue(v);
reversePost.push(v);
}
//返回遍历结果
public Iterable<Integer> pre(){ return pre; }
public Iterable<Integer> post(){ return post; }
public Iterable<Integer> reversePost(){ return reversePost; }
}
最后实现拓扑排序:
public class Tolpological {
private Iterable<Integer> order;
public Tolpological(Digraph G) {
DirectedCycle cyclefinder = new DirectedCycle(G);
//如果无有向环,则调用深度优先遍历,最后输出逆后序排列
if(!cyclefinder.hasCycle()) {
DepthFirstOrder dfs = new DepthFirstOrder(G);
order = dfs.reversePost();
}
}
public Iterable<Integer> order(){return order;}
}
一幅有向无环图的拓扑排序即为所有顶点的逆后序排序。
使用深度优先搜索对有向无环图进行拓扑排序需要的时间和V+E成正比。