用三色 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 判环"的好教程。

相关推荐
im_AMBER2 小时前
算法笔记 18 二分查找
数据结构·笔记·学习·算法
C雨后彩虹2 小时前
机器人活动区域
java·数据结构·算法·华为·面试
MarkHD3 小时前
车辆TBOX科普 第53次 三位一体智能车辆监控:电子围栏算法、驾驶行为分析与故障诊断逻辑深度解析
算法
苏小瀚3 小时前
[算法]---路径问题
数据结构·算法·leetcode
月明长歌3 小时前
【码道初阶】一道经典简单题:多数元素(LeetCode 169)|Boyer-Moore 投票算法详解
算法·leetcode·职场和发展
wadesir4 小时前
C语言模块化设计入门指南(从零开始构建清晰可维护的C程序)
c语言·开发语言·算法
t198751284 小时前
MATLAB水声信道仿真程序
开发语言·算法·matlab
云和恩墨4 小时前
AI驱动的Oracle SQL优化:从经验依赖到智能协同的三大价值
人工智能·sql·oracle·深度优先·dba
CoderYanger5 小时前
动态规划算法-简单多状态dp问题:15.买卖股票的最佳时机含冷冻期
开发语言·算法·leetcode·动态规划·1024程序员节