返回

细说「力扣 210」:课程表 II,拓扑排序轻松解决

前端

课程表 II:拓扑排序的经典应用

在「力扣 210」课程表 II 问题中,我们面临一个需要安排课程学习次序的难题。题目给定两个数组:一个数组 numCourses 表示课程数量,另一个数组 prerequisites 课程之间的依赖关系,即 prerequisites[i] = [ai, bi] 表示学习课程 ai 之前必须先学完课程 bi。我们的任务是找出一种合理的课程安排,使所有课程都能按正确的次序完成。

拓扑排序:化解依赖关系的利器

解决「力扣 210」的关键在于拓扑排序,这是一种用于解决有向无环图(Directed Acyclic Graph,简称 DAG)中节点排序问题的算法。拓扑排序的目的是为 DAG 中的节点建立一个线性序列,使得对于任何一对节点 uv,如果存在有向边 u → v,那么在序列中 u 总是排在 v 之前。

拓扑排序的原理和实现

拓扑排序通常采用两种主要方法:

  • 基于深度的搜索(DFS): 从一个未访问的节点开始,对图进行DFS。在访问过程中,记录每个节点的「进入时间」和「离开时间」。离开时间早于进入时间的节点可以安全加入拓扑序列。
  • 基于广度的搜索(BDS): 从所有入度为 0 的节点开始,对图进行 BDS。当访问一个节点时,其所有出边指向的节点入度减 1。当一个节点入度变为 0 时,它可以加入拓扑序列。

解决「力扣 210」的拓扑排序实现

基于「力扣 210」中给定的两个数组,我们使用一种基于深度的搜索方法来实现拓扑排序:

from collections import defaultdict,deque
def topological_sort(numCourses, prerequisites):
    # 初始化邻接表和入度表
    in_degree = [0] * numCourses
    graph = defaultdict(list)
    for pair in prerequisites:
        graph[pair[1]].append(pair[0])
        in_degree[pair[0]] += 1

    # 使用DFS进行拓扑排序
    stack = []
    visited = [False] * numCourses
    def dfs(node):
        visited[node] = True
        for neighbor in graph[node]:
            if not visited[neighbor]:
                dfs(neighbor)
        stack.append(node)

    for i in range(numCourses):
        if not visited[i]:
            dfs(i)
    return stack[::-1]

代码详解

  • 我们首先初始化一个邻接表 graph 来存储课程之间的依赖关系,以及一个入度表 in_degree 来记录每个课程的入度(即其他课程对其的依赖数量)。
  • 然后,我们使用一个基于深度的搜索函数 dfs 来对图进行拓扑排序。该函数通过使用一个栈 stack 来收集已访问的课程,从而生成拓扑序列。
  • dfs 函数中,我们首先将当前课程 node 访问状态设为已访问。然后,我们访问该课程的出边所指向的每个邻接课程 neighbor,并对其执行拓扑排序。
  • 最后,当我们访问完所有课程后,栈 stack 中的课程按拓扑次序排列,可以安全输出。

总结

通过使用拓扑排序算法,「力扣 210」课程表 II 问题可以得到高效且优雅的解决。拓扑排序为我们提供了一种有条理的方式来处理依赖关系问题,在软件工程、任务调度和其他领域有着重要的应用。