📜  门| GATE-CS-2017(套装1)|问题 13(1)

📅  最后修改于: 2023-12-03 14:58:31.996000             🧑  作者: Mango

问题 13

本题是关于图的基础知识和算法的。题目要求实现一段程序,最终输出有向图中所有的拓扑排序。下面我们来了解一下本题的详细内容。

规定

给出一个有向图 $G = (V, E)$,其中 $V$ 表示顶点的集合,$E$ 表示有向边的集合。图 $G$ 中有 $n$ 个顶点,用 $0$ 到 $n-1$ 的整数表示。${i, j}$ 表示从 $i$ 到 $j$ 的一条有向边。

拓扑排序的定义为一个顶点的线性排序,使得对于任何的一条从顶点 $i$ 到顶点 $j$ 的有向边,顶点 $j$ 在排列中都出现在顶点 $i$ 的后面。如果存在多个拓扑排序,则输出任意一个即可。

程序要求

请实现一个函数 print_topo_order(G),其中 $G$ 表示一个有向图。这个函数应该按字典序输出这个图的所有拓扑排序。

输入格式

第一行输入一个整数 $n$,表示有向图中有$n$个点。接下来 $n$ 行,第 $i$ 行表示从顶点 $i-1$ 向下标为 $a_{1, i}, a_{2, i}, \cdots, a_{k, i}$ 的顶点连有有向边,其中第一列 $a_{1, i}$ 表示顶点 $i$ 的出度。

输出格式

输出按照字典序排列的所有拓扑排序,每行一个排序。

举例说明

输入案例1:

3
2 2 3
1 3
0

输出案例1:

0 1 2
0 2 1

输入案例2:

3
1 2
2 3
1 3

输出案例2:

0 1 2
0 2 1
代码实现

我们可以采用拓扑排序的方法完成这个程序,使用拓扑排序需要用到一些数据结构,比如队列和数组。首先要将入度为0的点加入到队列里,然后依次对每个入度为0的点进行遍历,将以该点为起始点的边的重点的入度减1,同时将新的入度为0的点加入到队列里。

为了按照字典序排列,需要对结果进行排序。此处使用深度优先搜索的方法进行排序。首先从第一个点开始遍历,对于每个节点,遍历它所连接的所有点,并进行排序。遍历完点之后,将它加入到路径上,并继续对其它点进行遍历。如果某个点已经被加入到路径中,则跳过。当所有节点都被加入到路径中后,将路径输出即可。

下面是具体的实现代码:

from collections import deque


def print_topo_order(G):
    n = len(G)
    in_degree = [0] * n
    for u in range(n):
        for v in G[u]:
            in_degree[v] += 1

    d = deque([u for u in range(n) if in_degree[u] == 0])
    ans = []
    while d:
        u = d.popleft()
        ans.append(str(u))
        for v in G[u]:
            in_degree[v] -= 1
            if in_degree[v] == 0:
                d.append(v)

    def dfs(u, path):
        if u == n:
            ans.append(path)
            return
        for v in sorted(G[u]):
            if v in path: continue
            dfs(v, path + [v])

    for u in range(n):
        dfs(u, [u])

    return '\n'.join(ans)

需要注意的是,本题并没有说明图中是否有环,因此一定要进行环的检测,以防止陷入死循环。