有向图—任务调度拓扑图

1.有向图的数据类型

使用Bag表示有向图,其中边v->w表示为顶点v所对应的邻接链表中包含一个w顶点,与无向图不同的是,这里每条边只会出现一次.有向图的数据结构类型如下:

public class Digraph {
    private final int V;
    private int E;
    private Bag<Integer>[] adj;
    public Digraph(int V) {
        this.V=V;
        this.E=0;
        adj=(Bag<Integer>[])new Bag[V];
        for(int v=0;v<V;v++) {
            adj[v]=new Bag<Integer>();
        }
    }
    public int V() {
        return V;
    }
    public int E() {
        return E;
    }
    //添加一条边v->w,由于是有向图只要添加一条边就可以了
    public void addEdge(int v,int w) {
        adj[v].add(w);
        E++;
    }
    public Iterable<Integer> adj(int v) {
        return adj[v];
    }
    //返回当前图的一个反向的图
    public Digraph reverse() {
        Digraph R=new Digraph(V);
        for(int v=0;v<V;v++) {
            for(int w:adj(v)) {
                R.addEdge(w, v);
            }
        }
        return R;
    }
}

2.有向图中的可达性

无向图的连通性相似,同利用深度优先搜索可以解决有向图中

单点可达性问题:即:给定一幅有向图和一个起点s,回答是否存在一条从s到达给定顶点v的有向路径的问题.

多点可达性问题:给定一幅有向图和顶点的集合,回答是否存在一条从集合中的任意顶点到达给定顶点v的有向路径?

public class DirectedDFS {
    private boolean[] marked;
    //从G中找出所有s可达的点
    public DirectedDFS(Digraph G,int s) {
        marked=new boolean[G.V()];
        dfs(G,s);
    }
    //G中找出一系列点可达的点
    public DirectedDFS(Digraph G,Iterable<Integer> sources) {
        marked=new boolean[G.V()];
        for(int s:sources) {
            if(!marked[s]) dfs(G,s);
        }
    }
    //深度优先搜素判断.
    private void dfs(Digraph G, int v) {
        marked[v]=true;
        for(int w:G.adj(v)) {
            if(!marked[w]) dfs(G,w);
        }
    }
    //v是可达的吗
    public boolean marked(int v) {
        return marked[v];
    }
}

多点可达性问题的一个重要时机应用是在典型的内存管理系统中,包括许多java的实现。在一个有向图中,一个顶点表示一个对象,一条边则表示一个对象对另一个对象的引用。

这个模型很好表现了运行中的java程序的内存使用状况。在程序执行的任何时候都有某些对象是可以被直接访问的,而不能通过这些对象访问到的所有对象都应该被回收以便

释放内存。它会周期性的运行一个类似于DirectedDFS的有向图可达性算法来标记所有可以被访问到的对象。

3.有向图的寻路

和无向图类似,有向图中常见的问题:

单点有向路径。给定一幅有向图和一个起点,回答“从s到给定目的顶点v是否存在一条有向路径?如果有,找出这条路径”

单点最短有向路径。给定一幅有向图和一个起点,回答“从s到给定目的顶点v是否存在一条有向路径,如果有,找出其中最短的那条(所含边数最少)”

4.调度问题—拓扑排序

4.1寻找有向环

如果一个有优先限制的问题中存在有向环,那么这个问题肯定是无解的。所以需要进行有向环的检测。

下面的代码可以用来检测给定的有向图中是否含有有向环,如果有,则按照路径的方向返回环上的所有顶点.

在执行dfs的时候,查找的是从起点到v的有向路径,onStack数组标记了递归调用的栈上的所有顶点,同时也加入了edgeTo数组,在找到有向环的时候返回环中的所有顶点.

/**
 * 有向图G是否含有有向环
 * 获取有向环中的所有顶点
 * @author Administrator
 *
 */
public class DirectedCycle {
    private boolean[] marked;
    private int[] edgeTo;
    private Stack<Integer> cycle;    //有向环中的所有顶点
    private boolean[] onStack;        //递归调用的栈上的所有顶点

    public DirectedCycle(Digraph G) {
        edgeTo=new int[G.V()];
        onStack=new boolean[G.V()];
        marked=new boolean[G.V()];
        for(int v=0;v<G.V();v++) {
            if(!marked[v]) dfs(G,v);
        }
    }
/**
 * 该算法的关键步骤在于onStack数组的运用.
 * onStack数组标记的是当前遍历的点.如果对于一个点指向的所有点中的某个点
 * onstack[v]=true.代表该点正在被遍历也就是说
 * 该点存在一条路径,指向这个点.而这个点现在又可以指向该点,
 * 即存在环的结构~
 * @param G
 * @param v
 */
    private 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[w]) {
                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);
            }
        }
        //dfs方法结束,对于该点的递归调用结束.该点指向的所有点已经遍历完毕
        onStack[v]=false;
    }

    private boolean hasCycle() {
        return cycle!=null;
    }
    public Iterable<Integer> cycle() {
        return cycle;
    }
}

4.2 拓扑排序

拓补排序:给定一幅有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素.如果存在有向环的话,那么拓补排序无法完成.

  要实现有向图的拓补排序,利用标准深度优先搜索顺序即可完成任务.这里顶点会有三种排列顺序:

  1.前序:在递归调用前将顶点加入队列

  2.后序:在递归调用之后将顶点加入队列

  3.逆后序:在递归调用之后将顶点压入栈.

  具体的操作见下面的代码:

//有向图中基于深度优先搜索的拓补排序
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<>();
        post=new Queue<>();
        reversePost=new Stack<>();
        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;
    }
}

遍历的顺序取决于这个数据结构的性质以及是在递归调用之前还是之后进行保存。

前序:在递归调用之前将顶点加入队列。

后序:在递归调用之后将顶点加入队列。

逆后序:在递归调用之后将顶点压入栈。

前序就时dfs()的调用顺序;后序就是顶点遍历完成的顺序;逆后序就是顶点遍历完成顺序的逆。

拓补排序的实现依赖于上面的API,实际上拓补排序即为所有顶点的逆后序排列

拓补排序的代码如下:

public class Topological {
    private Iterable<Integer> order;    //顶点的拓补排序
    public Topological(Digraph G) {
        DirectedCycle cyclefinder=new DirectedCycle(G);
        if(!cyclefinder.hasCycle()) {//只有无环才能进行拓补排序
            DepthFirstOrder dfs=new DepthFirstOrder(G);
            order=dfs.reversePost();
        }
    }
    public Iterable<Integer> order() {
        return order;
    }
    public boolean isDAG() {
        return order!=null;
    }
}
时间: 2024-08-24 14:14:01

有向图—任务调度拓扑图的相关文章

拓扑图线条流动效果

图论中边是重要元素,它连接各个顶点构成拓扑图,有向图中,边具有方向性,在画布中表现为箭头,在实际应用中,边可以代表链路,链路上不只是有方向,还有流量,信号种类等信息,光用箭头表现力就不够了,可增加线条线型,以及流动效果来体现,这里介绍 Qunee 1.6 中线条流动效果的实现 虚线流动效果 虚线流动效果在 连线示例中有演示,使用虚线偏移量样式,不断增大,实现线条的流动 虚线流动代码 var offset = 0; var index = 0; var timer = setInterval(fu

分布式任务调度平台SIA-TASK的架构设计与运行流程

一.分布式任务调度的背景 无论是互联网应用或者企业级应用,都充斥着大量的批处理任务.我们常常需要一些任务调度系统来帮助解决问题.随着微服务化架构的逐步演进,单体架构逐渐演变为分布式.微服务架构.在此背景下,很多原先的任务调度平台已经不能满足业务系统的需求,于是出现了一些基于分布式的任务调度平台. 1.1 分布式任务调度的演进 在实际业务开发过程中,很多时候我们无可避免地需要使用一些定时任务来解决问题.通常我们会有多种解决方案:使用 Crontab 或 SpringCron (当然这种情况可能机器

宜信开源|分布式任务调度平台SIA-TASK的架构设计与运行流程

一.分布式任务调度的背景 无论是互联网应用或者企业级应用,都充斥着大量的批处理任务.我们常常需要一些任务调度系统来帮助解决问题.随着微服务化架构的逐步演进,单体架构逐渐演变为分布式.微服务架构.在此背景下,很多原先的任务调度平台已经不能满足业务系统的需求,于是出现了一些基于分布式的任务调度平台. 1.1 分布式任务调度的演进 在实际业务开发过程中,很多时候我们无可避免地需要使用一些定时任务来解决问题.通常我们会有多种解决方案:使用 Crontab 或 SpringCron (当然这种情况可能机器

开源分布式任务调度平台Cuckoo-Schedule

1         概述 1.1      平台概述 Cuckoo-Schedule是基于Quartz-Schedule的轻量级任务调度框架,具有易学习.易上手.开发高效稳定的特点.Demo地址:http://cuckoo.hellosr.com,测试用户:guest,密码:123456. Cuckoo-Schedule对调度模块与执行模块进行解耦,调度模块支持集部署.任务分组.任务依赖.权限管理.邮件告警.调度日志记录等功能,并提供WEB页面对任务进行管理,支持任务实时调度情况的查看.变更以及

任务调度框架-Quartz.Net

使用Quartz.Net依赖于以下3个组件:Common.Logging.dll.Common.Logging.Core.dll.Quartz.dll 简单封装 1 using Quartz; 2 using Quartz.Impl; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 na

有向图强连通分支的Tarjan算法讲解 + HDU 1269 连通图 Tarjan 结题报告

题目很简单就拿着这道题简单说说 有向图强连通分支的Tarjan算法 有向图强连通分支的Tarjan算法伪代码如下:void Tarjan(u) {dfn[u]=low[u]=++index//进行DFS,每发现一个新的点就对这个点打上时间戳,所以先找到的点时间戳越早,dfn[U]表示最早发现u的时间,low[u]表示u能到达的最早的时间戳.stack.push(u)//将U压入栈中for each (u, v) in E {if (v is not visted)//如果V点没有经历过DFS,则

hdu 4975 最大流及其唯一性判定(有向图环判断算法升级)

就当时最大流再次复习吧..动手敲一下...经典解法不想说了..这题主要是坑时间,10个提交7个tle. 环的判断,曾经用简单dfs方法,这次的就tle了!别人说要用很屌的dinic,我感觉自己dinic不可能超时,坚信是判断环慢了,于是学习了新断环的方法:删除点/边!从某点进去,若该点的所有边都遍历过还是无功而返,那么该店以后不用再进入了(这么简单的道感觉自己应该要想到啊!愚蠢啊!)开始时用只删除边,还是tle!nb!于是自己删点又删边,一下到156ms,前5了! #include<cstdio

java计划任务调度框架quartz结合spring实现调度的配置实例代码分享

点击链接加入群[JavaEE(SSH+IntelliJIDE+Maven)]:http://jq.qq.com/?_wv=1027&k=L2rbHv 一:quartz简介 OpenSymphony 的Quartz提供了一个比较完美的任务调度解决方案. Quartz 是个开源的作业调度框架,定时调度器,为在 Java 应用程序中进行作业调度提供了简单却强大的机制. Quartz中有两个基本概念:作业和触发器.作业是能够调度的可执行任务,触发器提供了对作业的调度 二:quartz spring配置详

java sql编辑器 动态报表 数据库备份还原 quartz定时任务调度 自定义表单 java图片爬虫

获取[下载地址]   QQ: 313596790   [免费支持更新] 三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体 [新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器(开发利器)+快速构建表单;            QQ:313596790 freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块 B 集成阿里巴巴数据库连接池druid;