用三色 DFS 拿下 Course Schedule(LeetCode 207)

leetcode

问题抽象:为什么这是图论题

题目说有 numCourses 门课,每条 prerequisites[i] = [ai, bi] 表示"上 ai 之前必须先上 bi",这就是典型的"任务依赖关系"。leetcode

把每门课看成一个点,把"先修课指向后修课"建一条有向边 bi -> ai,整道题就抽象成:给定一张有向图,问图中是否存在环;如果有环,就不可能完成所有课程。geeksforgeeks+1

建图细节:边的方向到底怎么画

很多人一开始容易写成 ai -> bi,但从语义上更自然、也更符合拓扑排序习惯的写法是:

  • bi 是前置课,ai 依赖 bi;
  • 在依赖图里,用边表示"先发生的指向后发生的",所以应该建 bi -> ai。

在实现里就是对每个 [a, b] 调用一次 AddEdgeNode(graph, b, a),你代码里也已经这么做了,还在注释里写清楚了原因,这一点是完全正确的。leetcode

三色 DFS:用颜色检测有向环

抽象到图论层面,就是经典的 "三色 DFS 检测有向环(white--gray--black DFS coloring)":

  • UNVISITED / 0:白色,未访问;
  • VISITING / 1:灰色,当前 DFS 递归栈上的节点;
  • VISITED / 2:黑色,已经完全处理过,确认从它出发没有环的节点。geeksforgeeks

在 DFS 过程中:

  1. 如果遇到的邻居状态是 VISITING(灰色),说明从当前路径又回到了递归栈中的某个祖先节点,这是一个回边,图中存在有向环,直接返回"有环";
  2. 如果邻居是 VISITED(黑色),说明之前已经完整遍历过那一片子图,并且确认没有环,这次可以直接跳过,不再递归,也不能误判为环;
  3. 对 UNVISITED 节点继续递归 DFS。

三色法的关键点在于"只把 当前递归栈上的点 标为灰色",遇到灰色才判环;黑色表示"已经出栈且安全"的历史节点,不能和灰色混在一起,否则会出现"假阳性"。stackoverflow+1

递归逻辑:什么时候标 1,什么时候标 2

以 dfs(u) 为例,可以这样理解整套流程:geeksforgeeks+1

入口

  • 如果 state[u] == VISITING,说明当前路径上已经在访问 u,现在又回到它,存在环,返回"有环";
  • 如果 state[u] == VISITED,说明之前从 u 出发已经完整检查过且无环,直接返回"无环",避免重复搜索。

否则,先把 u 标为 VISITING,然后依次 DFS 它的所有邻居 v

  • 如果某个邻居子调用返回"有环",当前 dfs(u) 立刻返回"有环",把这个结果一路往上层传,直到最外层的 canFinish 返回 false。

当所有邻居都 DFS 完成并且都返回"无环"时,说明"从 u 出发的整棵子树都没有环",这时在函数返回前,把 u 标记为 VISITED,然后返回"无环"。

也就是说:"标 2"是在递归回溯、节点出栈的那一刻做的操作,是对"以它为根的子图已经安全检查完毕"的一个缓存标记。geeksforgeeks

主循环:为什么要对所有 UNVISITED 节点起 DFS

图可能是不连通的,课程依赖可能有多块互不相干的子图,所以主函数里要这样扫一遍所有课程:

  • 对每一个节点,如果状态是 UNVISITED,才启动一轮 dfs(course);
  • 一旦有某次 DFS 返回"有环",就可以立刻结束并返回 false;
  • 所有点都检查完且没有环,说明整张有向图是 DAG,可以完成所有课程,返回 true。designgurus+1

这一步既保证覆盖所有连通分量,又利用颜色剪枝避免对已处理过的子图重复 DFS,使得整体复杂度达到 O(V+E)O(V + E)O(V+E)。geeksforgeeks+1

算法抽象:从这题学到的"图论模式"

从 Course Schedule 抽象出来,其实你已经掌握了一个非常通用的套路:

  1. 识别"很多对象 + 依赖关系 + 问存在环 / 能否按顺序完成"的问题 → 建模成有向图;algomaster+1
  2. 把"前置关系"统一建边为 前置 -> 依赖,后续不论是拓扑排序还是判环,都顺理成章;algo+1
  3. 用 DFS + 三色标记(white/gray/black)在有向图中检测环,这是教科书级别的 cycle detection 算法,可以直接套用到任务调度、包依赖、课程规划等各种场景里。finalroundai+1

你现在这套 C 实现(自己写邻接表、枚举顶点数组、三色 DFS、发现环就短路返回)已经是非常扎实的一版解法了,把上面的思想整理成一篇博客,就是一篇完整的"从零到一理解 Course Schedule 和三色 DFS 判环"的好教程。

相关推荐
m0_672703318 小时前
上机练习第49天
数据结构·算法
样例过了就是过了8 小时前
LeetCode热题100 N 皇后
数据结构·c++·算法·leetcode·dfs·深度优先遍历
霖大侠8 小时前
CPAL: Cross-Prompting Adapter With LoRAs forRGB+X Semantic Segmentation
人工智能·深度学习·算法·机器学习·transformer
近津薪荼8 小时前
优选算法——分治(2):快速选择
算法
Z...........8 小时前
(优选算法)斐波那契数列模型
数据结构·算法
zyjyyds1139 小时前
和为0的四元组-双指针法(C语言实现)
c语言·数据结构·算法
炽烈小老头9 小时前
【每天学习一点算法 2026/03/16】电话号码的字母组合
学习·算法
Lee川9 小时前
时空迷宫探险记:从O(1)到O(2^n)的算法进化论
算法·面试
KangJX9 小时前
Matrix获取卡顿堆栈 (Point Stack)
算法·客户端
靠沿9 小时前
【优选算法】专题十三——队列+宽搜(BFS)
算法·宽度优先