美文网首页
有关拓扑排序的一些个人理解

有关拓扑排序的一些个人理解

作者: 消失黎明 | 来源:发表于2019-06-16 15:51 被阅读0次

什么是 拓扑排序?百度百科中是这么定义的。
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
拓扑排序主要用来解决有向图中的依赖解析(dependency resolution)问题。

举例来说,如果我们将一系列需要运行的任务构成一个有向图,图中的有向边则代表某一任务必须在另一个任务之前完成这一限制。那么运用拓扑排序,我们就能得到满足执行顺序限制条件的一系列任务所需执行的先后顺序。当然也有可能图中并不存在这样一个拓扑顺序,这种情况下我们无法根据给定要求完成这一系列任务,这种情况称为循环依赖(circular dependency)。

拓扑排序存在的前提

当且仅当一个有向图为有向无环图(directed acyclic graph,或称DAG)时,才能得到对应于该图的拓扑排序。每一个有向无环图都至少存在一种拓扑排序。该论断可以利用反证法被证明如下:
假设我们有一由v_1v_n这n个结点构成的有向图,且图中v_1,v_2,...,v_n这些结点构成一个环。这即是说对于所有1≤i<n-1,图中存在一条有向边从v_i指向v_i+1。同时还存在一条从v_n指向v_1的边。假设该图存在一个拓扑排序。
那么基于这样一个有向图,显然我们可以得知对于所有1≤i<n-1v_i必须在v_i+1之前被遍历,也就是v_1必须在v_n之前被遍历。同时由于还存在一条从v_n指向v_1的边,v_n必须在v_1之前被遍历。这里出现了与我们的假设所冲突的结果。因此我们可以知道,该图存在拓扑排序的假设不成立。也就是说,对于非有向无环图而言,其拓扑排序不存在。

拓扑排序的算法和实现

拓扑排序的问题存在一个线性时间解。也就是说,若有向图中存在n个结点,则我们可以在O(n)时间内得到其拓扑排序,或在O(n)时间内确定该图不是有向无环图,也就是说对应的拓扑排序不存在。

根据图中的边的方向,我们可以看出,若要满足得到其拓扑排序,则结点被遍历的顺序必须满足如下要求:

  • 结点1必须在结点2、3之前
  • 结点2必须在结点3、4之前
  • 结点3必须在结点4、5之前
  • 结点4必须在结点5之前

则一个满足条件的拓扑排序为[1, 2, 3, 4, 5]。
若我们删去图中4、5结点之前的有向边,上图变为如下所示:


则我们可得到两个不同的拓扑排序结果:[1, 2, 3, 4, 5]和[1, 2, 3, 5, 4]。

为了说明如何得到一个有向无环图的拓扑排序,我们首先需要了解有向图结点的入度(indegree)出度(outdegree)的概念。

假设有向图中不存在起点和终点为同一结点的有向边。

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

在了解了入度和出度的概念之后,再根据拓扑排序的定义,我们自然就能够得出结论:要想完成拓扑排序,我们每次都应当从入度为 0 的结点开始遍历。因为只有入度为 0 的结点才能够成为拓扑排序的起点。否则根据拓扑排序的定义,只要一个结点v的入度不为 0,则至少有一条边起始于其他结点而指向v,那么这条边的起点在拓扑排序的顺序中应当位于v之前,则v不能成为当前遍历的起点。

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

  1. 初始化一个int[] inDegree保存每一个结点的入度。
  2. 对于图中的每一个结点的子结点,将其子结点的入度加1。
  3. 选取入度为0的结点开始遍历,并将该节点加入输出。
  4. 对于遍历过的每个结点,更新其子结点的入度:将子结点的入度减1。
  5. 重复步骤3,直到遍历完所有的结点。
  6. 如果无法遍历完所有的结点,则意味着当前的图不是有向无环图。不存在拓扑排序。
具体应用举例

LeetCode 207 - 课程表
使用BFS和拓扑排序解题,代码如下

class Solution(object):
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        if numCourses == 0:
            return True
        inDegrees = [0 for _ in range(numCourses)]
        adj = [set() for _ in range(numCourses)]
        for elem in prerequisites:
            first, second = elem[0], elem[1]
            inDegrees[second] += 1
            adj[first].add(second)
        res = []
        queue = []
        for i in range(numCourses):
            if inDegrees[i] == 0:
                queue.append(i)
        counter = 0
        while queue:
            tmp = queue.pop(0)
            counter += 1
            
            for successor in adj[tmp]:
                inDegrees[successor] -= 1
                if inDegrees[successor] == 0:
                    queue.append(successor)
        return numCourses == counter

相关文章

  • 有关拓扑排序的一些个人理解

    什么是 拓扑排序?百度百科中是这么定义的。对一个有向无环图(Directed Acyclic Graph简称DAG...

  • 算法学习之拓扑排序

    拓扑排序的理解 拓扑排序的定义: 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行...

  • 利用DFS实现拓扑排序

    拓扑排序定义利用“DAG必有零入度顶点”的特性,实现拓扑排序基于DFS搜索的拓扑排序 1. 拓扑排序定义 将一个有...

  • 图的最短路径和拓扑排序

    拓扑排序 2、图的最短路径 3、图的拓扑排序

  • 7.6图的应用:拓扑排序

    拓扑排序Topological Sort ❖从工作流程图得到工作次序排列的算法,称为“拓扑排序”❖拓扑排序处理一个...

  • 拓扑排序(Topological Sorting)

    拓扑排序(Topological Sorting)拓扑排序(Topological Sorting)是一个有向无环...

  • 判断有向图是否有环

    方法一:拓扑排序 时间复杂度O(n^2) 比较常用的是用拓扑排序来判断有向图中是否存在环。 什么是拓扑排序呢?我们...

  • 数据结构 图之拓扑排序

    拓扑排序 一、什么是拓扑排序? 在图论中,拓扑排序是一个有向无环图的所有顶点的线性序列,且该序列必须满足 每个顶点...

  • 拓扑排序(二)——拓扑排序

    LeetCode_210_CourseScheduleII 解法分析: 解法:

  • 18-拓扑排序

    拓扑排序## 拓扑排序是针对有向无环图定义的,此算法可以判断一个有向图是否存在回路。拓扑排序反应的是活动和工程的先...

网友评论

      本文标题:有关拓扑排序的一些个人理解

      本文链接:https://www.haomeiwen.com/subject/eodgfctx.html