拓扑排序(超详细!!!)

对于一个有向无环图来说拓扑排序的结果遵循如下规则,即如果有一条从顶点 v i v_i vi指向顶点 v j v_j vj的边,那么最后的排序结果中 v i v_i vi一定在 v j v_j vj的前面。例如

这个图的拓扑排序就是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\leq i < n 1i<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 T T存储拓扑排序结果。因此,对于一个拥有 N N N个顶点的图来说,我们先建立一个大小为 N 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 T 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):
    res, d, vis, q = [], [0]*N, [False]*N, []
    g = defaultdict(list)
    
    for i, j in pre:
        g[i].append(j)
        d[j] += 1
        
    for i in range(n):
        if d[i] == 0:
            q.append(i)
            vis[i] = True
    
    while q:
        cur = q.pop(0)
        N -= 1
        res.append(cur)
        for i in g[cur]:
            d[i] -= 1
            if not vis[i] and d[i] == 0:
                q.append(i)
                vis[i] = True
    return res if N == 0 else []

我们举一个例子看看这个算法是如何工作的,考虑下面这个图

一开始 i n _ d e g r e e [ 0 ] = 0 in\_degree[0]=0 in_degree[0]=0并且 T T T是空

所以我们从 q u e u e queue queue中删除0,将其添加到 T T T中。接着我们将所有0指向的点(123)的入度减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

接着我们将1 q u e u e queue queue中弹出,然后添加到 T T T中。接着我们将所有1指向的点(24)的入度减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

接着做类似操作

所以我们最终得到的拓扑排序数组是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 topSort(N, pre):
    res, vis = [], [0]*N
    g = defaultdict(list)
    
    for i, j in pre:
        g[i].append(j)
    
    def dfs(node):
        if vis[node] == 1:
            return False
        elif vis[node] == -1:
            return True
        
        vis[node] = 1
        for i in g[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

reference:

https://www.hackerearth.com/zh/practice/algorithms/graphs/topological-sort/tutorial/

如有问题,希望大家指出!!!

你可能感兴趣的:(Data,Structures,and,Algorithms)