SWE / Data Engineer · Coding

Meta Course Schedule:入度消除与环检测

用Kahn拓扑排序判断前置课程是否可完成,纠正图遍历复杂度和边方向。

题目重点:用Kahn拓扑排序判断前置课程是否可完成,纠正图遍历复杂度和边方向。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练课程0..n−1,pair(course,prerequisite)表示先修→课程;重复pair只一条依赖,自环不可完成。返回一个order或None,n0返回[]。
  • 无特定变体约束时按明确训练合同实现,BFS与DFS都合法;不声称DFS在所有输入上空间必更省。

解题路线

  1. 构adj[prerequisite]→course,indegree[course]加1。所有入度0课程共同入queue,每出队一个就删除其对后继的依赖。
  2. 后继入度到0才入队,保证全部前置已在结果。最终处理n个则成功;有剩余表示某环阻止消除,不是简单“还有没访问节点”。
  3. 为何剩余必有环?有限残余图若每节点入度>0,沿前置不断走必重复节点;无环图总有入度0起点。循环消除不会把环节点错误当完成。
  4. 边去重前不能重复增入度后却用set邻接只减一次,否则无环图会残余假环。ID与边方向先复述,小例子比复杂代码更能暴露写反。

手动走一遍

n3,(1,0),(2,1)可order[0,1,2];再加(0,2)成环返回None。两次同(1,0)仍一条前置,孤立课也必须出现在order。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

from collections import deque

def course_order(n,prerequisites):
    """Pair (course,prerequisite); duplicate edges dedup; cycle->None."""
    if n<0:raise ValueError('nonnegative course count')
    adjacent=[set() for _ in range(n)];indegree=[0]*n
    for course,prerequisite in prerequisites:
        if not 0<=course<n or not 0<=prerequisite<n:raise ValueError('course id')
        if course not in adjacent[prerequisite]:
            adjacent[prerequisite].add(course);indegree[course]+=1
    queue=deque(i for i,d in enumerate(indegree) if d==0);order=[]
    while queue:
        prerequisite=queue.popleft();order.append(prerequisite)
        for course in adjacent[prerequisite]:
            indegree[course]-=1
            if indegree[course]==0:queue.append(course)
    return order if len(order)==n else None

复杂度

O(V+E)时间、O(V+E)邻接/入度/queue,E按不同依赖边计。BFS不是O(VE);DFS递归栈最坏O(V),BFS队列最坏O(V),优劣依形状和运行时。

易错点与追问

容易失分的边界

  • pair方向写反会给逆序。
  • 重复edge的indegree与邻接存储不同步。

继续深挖

  • 要求字典序最小order时,用最小heap选择可用课程增加logV成本。
  • 要返回具体环,Kahn残余只能定位范围,需DFS颜色/父指针重构。
✓已复制微信号!