📜  门| GATE-CS-2006 |第 42 题(1)

📅  最后修改于: 2023-12-03 15:42:16.174000             🧑  作者: Mango

门 | GATE-CS-2006 |第 42 题

这是一道来自 GATE(Graduate Aptitude Test in Engineering)计算机科学考试 2006 年的题目,题号为 42。本题目考察的内容为图的遍历。

题目描述

给定一个有向图和一个起始节点,编写一个程序来查找从起始节点可到达的所有节点,并将它们按拓扑排序输出。

输入格式

输入的第一行包含两个整数 $N$ 和 $M$,分别表示图中的节点数量和边数量。接下来 $M$ 行,每行包含两个整数 $a$ 和 $b$,表示节点 $a$ 指向节点 $b$。

最后一行为一个整数 $s$,表示给定的起始节点。

此处省略了输入数据的处理代码。

输出格式

输出所有从起始节点可到达的节点(不包括起始节点),并按拓扑排序输出。如果有多个节点可以到达同一节点,则按照节点编号小的优先输出。每个节点占一行,按顺序输出。

解法

本题目需要对输入的有向图进行遍历,然后得到从起始节点出发能够到达的所有节点,最后按照拓扑排序输出。

遍历过程

我们可以使用广度优先搜索(BFS)来遍历有向图。在搜索过程中,如果一个节点没有被访问过,则将它加入到待访问队列中。每次从队列中取出队首元素并将它标记为访问过,然后将与队首元素直接相连而还未访问过的节点加入到队列末尾。

需要注意一个节点可能存在多个入度,因此我们使用一个字典或哈希表来记录每个节点的入度。

拓扑排序

拓扑排序是一种对有向无环图的节点进行排序的方法。在排序过程中,我们考虑将入度为 $0$ 的节点加入到结果列表中,并将它的后继节点的入度减 $1$。这样做完之后,我们从入度为 $0$ 的节点中再选择一个节点,重复上述操作,直到所有节点被加入到结果列表中或者发现图中存在环。

需要注意的是,在实现过程中需要保证结果列表中每个节点的后继节点都已加入到结果列表中,否则无法保证得到正确的拓扑排序结构。

代码实现

下面是本题目 Python 代码示例:

from collections import deque, defaultdict

def bfs(graph, start):
    # 初始化入度和是否访问字典
    indegrees = defaultdict(int)
    visited = defaultdict(bool)
    
    # 计算入度
    for u in graph:
        for v in graph[u]:
            indegrees[v] += 1
    
    # 存入起始节点
    queue = deque([start])
    visited[start] = True
    
    while queue:
        node = queue.popleft()
        for neighbour in graph[node]:
            indegrees[neighbour] -= 1
            if not visited[neighbour] and indegrees[neighbour] == 0:
                queue.append(neighbour)
                visited[neighbour] = True
    
    # 返回遍历后的结果列表
    result = []
    for node, is_visited in visited.items():
        if is_visited and node != start:
            result.append(node)
    
    return result

def top_sort(graph):
    # 初始化入度字典和结果列表
    indegrees = defaultdict(int)
    result = []
    
    # 计算入度
    for u in graph:
        for v in graph[u]:
            indegrees[v] += 1
    
    # 存入入度为 0 的节点
    queue = deque([node for node in graph if indegrees[node] == 0])
    
    while queue:
        node = queue.popleft()
        result.append(node)
        for neighbour in graph[node]:
            indegrees[neighbour] -= 1
            if indegrees[neighbour] == 0:
                queue.append(neighbour)
    
    # 如果存在未访问节点,则有环
    for node in graph:
        if indegrees[node] != 0:
            return None
    
    return result

# 读入输入
n, m = map(int, input().split())
graph = defaultdict(list)
for i in range(m):
    u, v = map(int, input().split())
    graph[u].append(v)
start = int(input())

# 获取所有能够从起始节点到达的节点
reachable_nodes = bfs(graph, start)

# 将图上的所有节点计入拓扑排序
sort_result = top_sort(graph)

# 输出所有从起始节点能够到达的拓扑排序的节点
print("\n".join([str(node) for node in sort_result if node in reachable_nodes]))

在本题目中,我们首先使用 bfs() 函数计算出从起始节点 start 出发可达到的所有节点,然后再使用 top_sort() 函数对图进行拓扑排序,并将搜索结果和拓扑排序结果进行比较,输出所有从起始节点能够到达的拓扑排序的节点。

复杂度分析

本题目中使用了 BFS 和拓扑排序两个算法,因此时间复杂度为 $O(m + n)$,其中 $m$ 表示边数,$n$ 表示节点数。空间复杂度为 $O(n)$,用于存储入度字典和队列。