点击上方蓝字关注DolphinScheduler(海豚调度)

|作者:代立冬

|编辑:闫利帅

回顾基础知识:

  • 图的遍历

图的遍历是指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次且仅访问一次。

        注意树是一种特殊的图,所以树的遍历实际上也可以看作是一种特殊的图的遍历

  • 图的遍历主要有两种算法

    • 广度优先搜索(Breadth First Search,BFS)

      深度优先搜索的搜索策略是尽可能深地搜索一个图。基本思想是:首先访问图中某一未访问的顶点V1,然后由V1出发,访问与V1邻接且未被访问的任一顶点V2,再访问与V2邻接且未被访问的任一顶点V3,……重复上述过程。当不能再继续向下访问(即孤立点)时,依次退回到最近被访问的顶点,若它还有邻接顶点未被访问过,则从该点开始继续上述搜索过程,直到图中所有顶点均被访问过为止。

    • 深度优先搜索(Depth First Search,DFS)

      广度优先搜索的基本思想是:首先访问起始顶点v,接着由v出发,依次访问v的各个未访问过的邻接顶点w1,w2,…,wi,然后再依次访问w1,w2,…,wi的所有未被访问过的邻接顶点;再从这些访问过的顶点出发,再访问它们所有未被访问过的邻接顶点……依次类推,直到图中所有顶点都被访问过为止。

举例说明

如下图,如果采用 广度优先搜索(BFS)遍历如下 1 2 5 3 4 6 7,如果采用深度优先搜索(DFS)遍历如下 1 2 3 4 5 6 7

拓扑排序(Topological Sorting)

维基百科上拓扑排序的定义为

对于任何有向无环图(Directed Acyclic Graph,DAG)而言,其拓扑排序为其所有结点的一个线性排序(同一个有向图可能存在多个这样的结点排序)。该排序满足这样的条件——对于图中的任意两个结点U和V,若存在一条有向边从U指向V,则在拓扑排序中U一定出现在V前面。

通俗来讲:拓扑排序是一个有向无环图(DAG)的所有顶点的线性序列, 该序列必须满足两个条件

  • 每个顶点出现且只出现一次。

  • 若存在一条从顶点A到顶点B的路径,那么在序列中顶点 A出现在顶点 B的前面。

如何找出它的拓扑排序呢?这里说一种比较常用的方法:

在介绍这个方法之前有必要补充下有向图结点的入度(indegree)和出度(outdegree)的概念。假设有向图中不存在起点和终点为同一结点的有向边,则:

入度:设有向图中有一结点V,其入度即为当前所有从其他结点出发,终点为V的的边的数目。也就是所有指向V的有向边的数目。

出度:设有向图中有一结点V,其出度即为当前所有起点为V,指向其他结点的边的数目。也就是所有由V发出的边的数目。

  1. 从DAG图中选择一个入度为0的顶点并输出。

  2. 从图中删除该顶点和所有以它为起点的有向边。

  3. 重复1和2直到当前的DAG图为空或当前图中不存在入度为0的顶点为止。后一种情况说明有向图中必然存在环。

例如下面这个DAG图:

结点1的入度:0,出度:2

结点2的入度:1,出度:2

结点3的入度:2,出度:1

结点4的入度:2,出度:2

结点5的入度:2,出度:0

它的拓扑排序流程为:

于是,得到拓扑排序后的结果是: {1,2,4,3,5} 。

如果没有结点2 —> 结点4的这个箭头,那么如下:

我们可以得到它的拓扑排序为:{1,2,4,3,5} 或者 {1,4,2,3,5} ,即对同一DAG图来说,它的拓扑排序结果可能存在多个。

拓扑排序主要用来解决有向图中的依赖问题。

在讲到实现的时候,有必要插以下内容:

由此我们可以进一步得出一个改进的深度优先遍历或广度优先遍历算法来完成拓扑排序。以广度优先遍历为例,这一改进后的算法与普通的广度优先遍历唯一的区别在于我们应当保存每一个结点对应的入度,并在遍历的每一层选取入度为0的结点开始遍历(而普通的广度优先遍历则无此限制,可以从该吃呢个任意一个结点开始遍历)。这个算法描述如下:

  1. 初始化一个Map或者类似数据结构来保存每一个结点的入度。

  2. 对于图中的每一个结点的子结点,将其子结点的入度加1。

  3. 选取入度为0的任意一个结点开始遍历,并将该节点加入输出。

  4. 对于遍历过的每个结点,更新其子结点的入度:将子结点的入度减1。

  5. 重复步骤3,直到遍历完所有的结点。

  6. 如果无法遍历完所有的结点,则意味着当前的图不是有向无环图。不存在拓扑排序。

广度优先遍历拓扑排序的核心Java代码如下:

public class TopologicalSort {
  /**
   * 判断是否有环及拓扑排序结果
   *
   * 有向无环图(DAG)才有拓扑(topological)排序
   * 广度优先遍历的主要做法:
   *    1、遍历图中所有的顶点,将入度为0的顶点入队列。
   *    2、从队列中poll出一个顶点,更新该顶点的邻接点的入度(减1),如果邻接点的入度减1之后等于0,则将该邻接点入队列。
   *    3、一直执行第2步,直到队列为空。
   * 如果无法遍历完所有的结点,则意味着当前的图不是有向无环图。不存在拓扑排序。
   *
   *
   * @return key返回的是状态, 如果成功(无环)为true, 失败则有环, value为拓扑排序结果(可能是其中一种)
   */
  private Map.Entry<Boolean, List<Vertex>> topologicalSort() {
 //入度为0的结点队列
    Queue<Vertex> zeroIndegreeVertexQueue = new LinkedList<>();
    //保存结果
    List<Vertex> topoResultList = new ArrayList<>();
    //保存入度不为0的结点
    Map<Vertex, Integer> notZeroIndegreeVertexMap = new HashMap<>();     //扫描所有的顶点,将入度为0的顶点入队列
    for (Map.Entry<Vertex, VertexInfo> vertices : verticesMap.entrySet()) {
      Vertex vertex = vertices.getKey();
      int inDegree = getIndegree(vertex);       if (inDegree == 0) {
        zeroIndegreeVertexQueue.add(vertex);
        topoResultList.add(vertex);
      } else {
        notZeroIndegreeVertexMap.put(vertex, inDegree);
      }
    }
    
 //扫描完后,没有入度为0的结点,说明有环,直接返回
    if(zeroIndegreeVertexQueue.isEmpty()){
      return new AbstractMap.SimpleEntry(false, topoResultList);
    }     //采用topology算法, 删除入度为0的结点和它的关联边
    while (!zeroIndegreeVertexQueue.isEmpty()) {
      Vertex v = zeroIndegreeVertexQueue.poll();
      //得到相邻结点
      Set<Vertex> subsequentNodes = getSubsequentNodes(v);       for (Vertex subsequentVertex : subsequentNodes) {         Integer degree = notZeroIndegreeVertexMap.get(subsequentVertex);         if(--degree == 0){
          topoResultList.add(subsequentVertex);
          zeroIndegreeVertexQueue.add(subsequentVertex);
          notZeroIndegreeVertexMap.remove(subsequentVertex);
        }else{
          notZeroIndegreeVertexMap.put(subsequentVertex, degree);
        }       }
    }     //notZeroIndegreeVertexMap如果为空, 表示没有环
    AbstractMap.SimpleEntry resultMap = new AbstractMap.SimpleEntry(notZeroIndegreeVertexMap.size() == 0 , topoResultList);
    return resultMap;   }
}

注意输出结果是该图的拓扑排序序列之一。

每次在入度为0的集合中取顶点,并没有特殊的取出规则,取顶点的顺序不同会得到不同的拓扑排序序列(如果该图有多种排序序列)。

由于输出每个顶点的同时还要删除以它为起点的边。如果图有V个顶点,E条边,则一般该算法的时间复杂度为O(V+E)。这里实现的算法最终key返回的是状态, 如果成功(无环)为true, 失败则有环, 无环时value为拓扑排序结果(可能是其中一种)。注意输出结果是该图的拓扑排序序列之一。每次在入度为0的集合中取顶点,并没有特殊的取出规则,取顶点的顺序不同会得到不同的拓扑排序序列(如果该图有多种排序序列)。

由于输出每个顶点的同时还要删除以它为起点的边。如果图有V个顶点,E条边,则一般该算法的时间复杂度为O(V+E)。这里实现的算法最终key返回的是状态, 如果成功(无环)为true, 失败则有环, 无环时value为拓扑排序结果(可能是其中一种)。

他们正在使用Apache DolphinScheduler

欢迎在此处阅读原文,点star收藏

大数据工作流任务调度--有向无环图(DAG)之拓扑排序的更多相关文章

  1. 第十二届湖南省赛 (B - 有向无环图 )(拓扑排序+思维)好题

    Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始.点 v 结束的路径). 为了方便,点用 1,2,…,n 编号. 设 count(x,y) 表示点 x 到点 y ...

  2. C#实现有向无环图(DAG)拓扑排序

    对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在 ...

  3. 【模板整合计划】图论—有向无环图 (DAG) 与树

    [模板整合计划]图论-有向无环图 (DAG) 与树 一:[拓扑排序] 最大食物链计数 \(\text{[P4017]}\) #include<cstring> #include<cs ...

  4. 判断有向无环图(DAG)

    1.拓扑排序 bfs 所有入度为0的先入选. 2.tarjan 1个点1个集合 3.暴力 一个点不能重新到达自己

  5. [笔记] 有向无环图 DAG

    最小链覆盖 (最长反链) 最小链覆盖 \(=n-\) 最大匹配. 考虑首先每个点自成一条链,此时恰好有 \(n\) 条链,最终答案一定是合并(首尾相接)若干条链形成的. 将两点匹配的含义其实就是将链合 ...

  6. 【拓扑】【宽搜】CSU 1084 有向无环图 (2016湖南省第十二届大学生计算机程序设计竞赛)

    题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1804 题目大意: 一个有向无环图(DAG),有N个点M条有向边(N,M<=105 ...

  7. JavaScript + SVG实现Web前端WorkFlow工作流DAG有向无环图

    一.效果图展示及说明 (图一) (图二) 附注说明: 1. 图例都是DAG有向无环图的展现效果.两张图的区别为第二张图包含了多个分段关系.放置展示图片效果主要是为了说明该例子支持多段关系的展现(当前也 ...

  8. select 函数实现 三种拓扑结构 n个客户端的异步通信 (完全图+线性链表+无环图)

    一.这里只介绍简单的三个客户端异步通信(完全图拓扑结构) //建立管道 mkfifo open顺序: cl1 读 , cl2 cl3 向 cl1写 cl2 读 , cl1 cl3 向 cl2写 cl3 ...

  9. 算法精解:DAG有向无环图

    DAG是公认的下一代区块链的标志.本文从算法基础去研究分析DAG算法,以及它是如何运用到区块链中,解决了当前区块链的哪些问题. 关键字:DAG,有向无环图,算法,背包,深度优先搜索,栈,BlockCh ...

随机推荐

  1. Zookeeper安装学习(二)

    学习内容:Zookeeper集群安装(Zookeeper版本:Zookeeper3.5.7:注:master,s1,s2都需要部署) 解压安装: (1)在主机 master 解压 Zookeeper ...

  2. TKE qGPU 通过 CRD 管理集群 GPU 卡资源

    作者 刘旭,腾讯云高级工程师,专注容器云原生领域,有多年大规模 Kubernetes 集群管理经验,现负责腾讯云 GPU 容器的研发工作. 背景 目前 TKE 已提供基于 qGPU 的算力/显存强隔离 ...

  3. ML第4周学习小结

    本周收获 总结一下本周学习内容: 1.学习了<深入浅出Pandas>的第五章:Pandas高级操作的两个内容 添加修改数据 高级过滤 我的博客链接: Pandas:添加修改.高级过滤 2. ...

  4. 【摸鱼神器】UI库秒变LowCode工具——列表篇(一)设计与实现

    内容摘要: 需求分析 定义 interface 定义 json 文件 定义列表控件的 props 基于 el-table 封装,实现依赖 json 渲染 实现内置功能:选择行(单选.多选),格式化.锁 ...

  5. Spring Ioc源码分析系列--容器实例化Bean的四种方法

    Spring Ioc源码分析系列--实例化Bean的几种方法 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到bean真正通过那些方式实例化出来的时候,并没有继续分 ...

  6. 我的 Java 学习&面试网站又又又升级了!

    晚上好,我是 Guide. 距离上次介绍 JavaGuide 新版在线阅读网站已经过去 7 个多月了(相关阅读:官宣!我升级了!!!),这 7 个多月里不论是 JavaGuide 的内容,还是 Jav ...

  7. Jmeter(五十四) - 从入门到精通高级篇 - 如何在linux系统下运行jmeter脚本 - 上篇(详解教程)

    1.简介 上一篇宏哥已经介绍了如何在Linux系统中安装Jmeter,想必各位小伙伴都已经在Linux服务器或者虚拟机上已经实践并且都已经成功安装好了,那么今天宏哥就来介绍一下如何在Linux系统下运 ...

  8. Vscode个性化设置:让一个小萌妹陪你敲代码

    前言 大家平时都用什么代码编辑器啊!我个人比较喜欢用vscode,因为有以下几点: 开源,免费: 自定义配置 集成git 智能提示强大 支持各种文件格式(html/jade/css/less/sass ...

  9. RPA 快手自动上传机器人

    1.打开账号Cookie预存表格 2.机器人自动登录账号 3.机器人开始按照预设视频位置开始自动上传视频 4.机器人开始自动填写视频相关信息内容 5.完成后,可自动切换下一个账号继续上传

  10. 动态树 — Euler_Tour_Tree

    一般提到动态树,我们会不约而同的想到 LCT,这算是比较通用,实用,能力较为广泛的一种写法了.当然,掌握 LCT 就需要熟悉掌握 Splay 和各种操作和知识.ETT(中文常用称呼:欧拉游览树)是一种 ...