Skip to main content

210.课程安排 II

标签: graph, topological-sort, depth-first-search, breadth-first-search

难度: Medium

通过率: 52.09%

原题链接: https://leetcode.com/problems/course-schedule-ii/description/

题目描述

给定一个表示课程先后顺序的数组 prerequisites,其中 prerequisites[i] = [a_i, b_i] 表示在选修课程 a_i 之前,你必须修完课程 b_i 。返回课程的一个排序,以便你可以顺利完成所有课程。如果不可能完成所有课程,返回一个空数组。

解题思路

问题本质上是寻找一个有向图的拓扑排序。若存在一个拓扑排序,则可以完成所有课程。否则,说明图中存在环,无法完成课程。我们可以通过以下两种方法解决问题:

  1. 深度优先搜索(DFS): 使用DFS检测图中的环,并生成一个拓扑排序。

    • 使用一个状态数组,记录每个节点的状态:未访问,访问中,已访问。
    • 如果在进行DFS的过程中再次访问到一个访问中的节点,说明存在环。
    • 如果不存在环,每次DFS完成一个节点后,将其加入结果数组。
  2. 广度优先搜索(BFS, Kahn's Algorithm): 使用入度数组和队列实现。

    • 初始化每个节点的入度,并将入度为0的节点加入队列。
    • 每次从队列中取出入度为0的节点,加入结果数组,并在图中移除该节点,减少其邻接节点的入度。
    • 如果最终结果数组中含有所有节点,则表示存在拓扑排序,否则表示存在环。

代码实现

def findOrder(numCourses, prerequisites):
# 构建邻接表和入度数组
from collections import defaultdict, deque
adj_list = defaultdict(list)
indegree = [0] * numCourses

# 填充邻接表和入度数组
for dest, src in prerequisites:
adj_list[src].append(dest)
indegree[dest] += 1

# 初始化队列,将所有入度为0的节点入队
zero_indegree_queue = deque([k for k in range(numCourses) if indegree[k] == 0])
topological_sorted_order = []

# 处理队列中的节点
while zero_indegree_queue:
vertex = zero_indegree_queue.popleft()
topological_sorted_order.append(vertex)
# 减少相邻节点的入度
for neighbor in adj_list[vertex]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
zero_indegree_queue.append(neighbor)

if len(topological_sorted_order) == numCourses:
return topological_sorted_order
else:
return []

复杂度分析

时间复杂度:O(V+E)O(V + E),其中 VV 是课程数量,EE 是先修课要求的数量。因为我们需要遍历每个节点和每条边。

空间复杂度:O(V+E)O(V + E),用于表示图的邻接表和保存每个节点的入度信息。