前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >有向图----有向环检测和拓扑排序

有向图----有向环检测和拓扑排序

作者头像
SuperHeroes
发布2018-05-30 17:57:28
3.3K3
发布2018-05-30 17:57:28
举报
文章被收录于专栏:云霄雨霁云霄雨霁

上一篇:有向图的深度优先和广度优先遍历

优先级限制下的调度问题:给定一组需要完成的任务,以及一组关于任务完成的先后次序的优先级限制。在满足限制条件的前提下应该如何安排并完成所有任务?

拓扑排序:给定一幅有向图,将所有顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素(或者说明无法做到这一点)。

优先级限制下不应该存在有向环,一个优先级限制的问题如果存在有向环,那么这个问题 肯定是无解的。

先来解决有向环检测问题

采用深度优先遍历来解决这个问题:用一个栈表示“当前”正在遍历的有向路径上的顶点。一旦找到一条有向边v->w,并且w已经存在于栈中,那么就找到了一个环;如果没有找到这条边,那么就是无环图。

实现:

代码语言:javascript
复制
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;  }
}

其实,将有向无环图的深度优先遍历的路径记录下来就是一种拓扑排序!遍历的顺序取决于数据结构的性质以及是在递归调用之前还是之后保存。一般有三种排列排序:

  • 前序:在递归调用之前将顶点加入队列
  • 后序:在递归调用之后将顶点加入队列
  • 逆后序:在递归调用之后将顶点压入栈

基于深度优先搜索的顶点排序

代码语言:javascript
复制
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;  }
}

最后实现拓扑排序:

代码语言:javascript
复制
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成正比。

下一篇:有向图的强连通分量问题

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.12.21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档