LeetCode P210 课程表 II

核心思想

此问题本质上是在一个有向图中寻找一个拓扑排序。每门课程是一个图的节点,课程间的先修关系 [a, b] (想学 a 必须先学 b) 可以看作一条从 ba有向边 b -> a

我们的目标是输出一个所有节点的线性序列,使得对于任意边 b -> a,节点 b 都在节点 a 之前。如果图中存在(例如,A依赖B,B依赖A),则说明无法完成所有课程,不存在拓扑排序。

本题解采用深度优先搜索 (DFS) 来实现拓扑排序和环检测。

算法思路

图的构建

使用邻接表 adj 来表示图。adj[i] 存储所有以课程 i 为先修课的课程列表。例如,对于 [a, b],我们将 a 添加到 adj[b] 中。

节点状态

为了在 DFS 中检测环并构建拓扑序列,我们为每个节点(课程)维护一个状态 visited

  • 0 (未访问):初始状态,表示该节点还未被访问。
  • 1 (访问中):表示该节点已经在此次 DFS 的递归路径上,但其所有邻接点尚未完全探索。
  • 2 (已访问):表示该节点及其所有邻接点都已被完全访问。

DFS 过程

从一个未访问的节点开始进行 DFS。首先将其状态标记为 1 (访问中)。遍历其所有邻接点:

  • 如果邻接点是 0 (未访问),则对其进行递归 DFS。
  • 如果邻接点是 1 (访问中),说明我们从当前节点出发,又回到了同一条递归路径上的某个节点,这表明图中存在。立即停止并标记有环。
  • 如果邻接点是 2 (已访问),说明它已被安全访问过,无需处理。

当一个节点的所有邻接点都访问完毕后,将其状态标记为 2 (已访问),并将其追加到结果列表 result 的末尾

结果生成

遍历所有课程,对每个状态为 0 的课程调用 DFS。如果在任何一次 DFS 中检测到环,直接返回空数组 []

由于 DFS 是在访问完所有后续课程后才将当前课程加入 result,所以 result 中课程的顺序是拓扑排序的逆序。因此,最终返回 result 的反转列表 result[::-1]

代码解析

from typing import List
class Solution:
    def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
        # 1. 初始化邻接表、访问状态数组和结果列表
        adj = [[] for _ in range(numCourses)]
        # 构建邻接表: b -> a
        for course, prereq in prerequisites:
            adj[prereq].append(course)
        
        visited = [0] * numCourses  # 0: 未访问, 1: 访问中, 2: 已访问
        result = []
        has_cycle = False

        def dfs(course: int):
            nonlocal has_cycle
            # 标记为访问中
            visited[course] = 1
            
            for neighbor in adj[course]:
                if visited[neighbor] == 0:
                    dfs(neighbor)
                    if has_cycle: return  # 如果下游检测到环,提前返回
                elif visited[neighbor] == 1:
                    # 发现环,直接标记并返回
                    has_cycle = True
                    return
            
            # 3. 所有邻接点访问完毕,标记为已访问,并加入结果列表
            visited[course] = 2
            result.append(course)

        # 4. 遍历所有课程,确保处理图中所有连通分量
        for i in range(numCourses):
            if visited[i] == 0:
                dfs(i)
                if has_cycle:
                    return []  # 检测到环,无法完成课程

        # 5. DFS 得到的是逆后序遍历,反转即为拓扑排序
        return result[::-1]
posted @ 2025-08-05 21:48  AFewMoon  阅读(12)  评论(0)    收藏  举报