拓扑排序 原理介绍 - Topological Sort Introduction

文章目录

  • 拓扑排序的典型应用
  • 拓扑排序的求解方法
  • reference

拓扑排序的典型应用

  • 修课顺序
  • 项目编译顺序

拓扑排序的求解方法

对于一个有向无环图来说拓扑排序的结果遵循如下规则,即如果有一条从顶点 v i v_i vi指向顶点 v j v_j vj的边,那么最后的排序结果中 v i v_i vi一定在 v j v_j vj 的前面。例如
拓扑排序 原理介绍 - Topological Sort Introduction_第1张图片
这个图的拓扑排序就是1 2 3 4 5

同一个图可以有多个拓扑排序结果,对于上面那个图,还有一种排序结果就是1 2 3 5 4

为了可以进行拓扑排序,给定的图中不可以有环。我们可以简单论证一下,假设图中的顶点为 v 1 , v 2 , v 3 . . . v n v_1,v_2,v_3...v_n v1,v2,v3...vn ,并且这些点构成环,也就是说有一条边从 v i v_i vi指向 v i + 1 v_{i+1} vi+1(其中 1 ≤ i < n 1≤i1i<n)并且有一条边从 v n v_{n} vn指向 v 1 v_1 v1。那么现在我们进行拓扑排序的话, v n v_n vn一定要排在 v 1 v_1 v1之前,因为有一条 v n v_n vn指向 v 1 v_1 v1的边。而 v i v_{i} vi一定排在 v i + 1 v_{i+1} vi+1的前面,因为有一条从 v i v_{i} vi指向 v i + 1 v_{i+1} vi+1的边。那么这意味着 v 1 v_1 v1排在 v n v_n vn前面,所以这和前面矛盾。

接着我们思考一下如何找到一个图的拓扑排序。最基本的思路就是找到一组顶点的排列,使得所有指向 v j v_j vj的顶点 v i v_i vi,都排在顶点 v j v_j vj的前面。我们维护一个数组T存储拓扑排序结果。

因此,对于一个拥有N个顶点的图来说,我们先建立一个大小为N的数组 i n _ d e g r e e [ ] in\_degree[] in_degree[],其 i t h i^{th} ith元素表示 v i v_i vi的入度(也就是所有指向 v i v_i vi结点的数量)。我们将顶点 v i v_i vi插入到T中的同时,要将 i n _ d e g r e e [ v j ] in\_degree[v_j] in_degree[vj]1(其中 v j v_j vj表示 v i v_i vi指向的所有点)。在任何时候我们都只能插入 i n _ d e g r e e [ ] in\_degree[] in_degree[]0的顶点。

  • BFS代码
from collections import defaultdict

def topSort(N, pre):
    """
    N 为 顶点数量
    pre: List[List[int]] 为关系数组
    """
    res, in_degree, vis, queue = [], [0]*N, [False]*N, []
    graph = defaultdict(list)
    
    for i, j in pre:
        graph[i].append(j)
        in_degree[j] += 1
        
    for i in range(N):
        if in_degree[i] == 0:
            queue.append(i)
            vis[i] = True
    
    while queue:
        cur = queue.pop(0)
        N -= 1
        res.append(cur)
        for i in graph[cur]:
            in_degree[i] -= 1
            if not vis[i] and in_degree[i] == 0:
                queue.append(i)
                vis[i] = True
    return res if N == 0 else []

我们举一个例子看看这个算法是如何工作的,考虑下面这个图
拓扑排序 原理介绍 - Topological Sort Introduction_第2张图片
一开始 i n _ d e g r e e [ 0 ] = 0 in\_degree[0]=0 in_degree[0]=0并且 T T T是空
拓扑排序 原理介绍 - Topological Sort Introduction_第3张图片
所以我们从 q u e u e queue queue中删除0,将其添加到 T T T中。接着我们将所有0指向的点(1,2,3)的入度减1。现在 i n _ d e q u e [ 1 ] = 0 in\_deque[1]=0 in_deque[1]=0,我们将1添加到 q u e u e queue queue
拓扑排序 原理介绍 - Topological Sort Introduction_第4张图片
接着我们将1从 q u e u e queue queue中弹出,然后添加到T中。接着我们将所有1指向的点(2,4)的入度减1。现在 i n _ d e q u e [ 2 ] = 0 in\_deque[2]=0 in_deque[2]=0,我们将2添加到 q u e u e queue queue
拓扑排序 原理介绍 - Topological Sort Introduction_第5张图片
接着做类似操作
拓扑排序 原理介绍 - Topological Sort Introduction_第6张图片
所以我们最终得到的拓扑排序数组是0,1,2,3,4,5

  • 我们也可以通过DFS来处理该问题,使用DFS的话,我们就不用建立 i n _ d e g r e e in\_degree in_degree数组了。
  • 下面是DFS的代码
from collections import defaultdict
def DFS_topSort(N, pre):
    res, vis = [], [0]*N
    graph = defaultdict(list)
    
    for i, j in pre:
        graph[i].append(j)  # graph[i]的值为一个列表,存储着 指向i的所有顶点
    
    def dfs(node):
        if vis[node] == 1:
            return False
        elif vis[node] == -1:
            return True
        
        vis[node] = 1
        for i in graph[node]:
            if not dfs(i):
                return False
        res.insert(0, node)
        vis[node] = -1
        return True
    
    for i in range(N):
        if vis[i] == 0 and not dfs(i):
            return []
    return res

N = 3
relations = [[0,2],[1,2]]
print(DFS_topSort(N, relations))

reference

https://blog.csdn.net/qq_17550379/article/details/97610885
topological-sort

你可能感兴趣的:(LeetCode题解,拓扑排序)