news 2026/10/11 7:43:00

207. 课程表

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
207. 课程表

207. 课程表

中等

提示

你这个学期必须选修numCourses门课程,记为0到numCourses - 1。

在选修某些课程之前需要一些先修课程。 先修课程按数组prerequisites给出,其中prerequisites[i] = [ai, bi],表示如果要学习课程ai则必须先学习课程bi。

  • 例如,先修课程对[0, 1]表示:想要学习课程0,你需要先完成课程1。

请你判断是否可能完成所有课程的学习?如果可以,返回true;否则,返回false。

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]] 输出:true 解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

示例 2:

输入:numCourses = 2, prerequisites = [[1,0],[0,1]] 输出:false 解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。

提示:

  • 1 <= numCourses <= 2000
  • 0 <= prerequisites.length <= 5000
  • prerequisites[i].length == 2
  • 0 <= ai, bi < numCourses
  • prerequisites[i]中的所有课程对互不相同

📝 核心笔记:课程表 (拓扑排序 / DFS 找环)

1. 核心思想 (一句话总结)

“红绿灯机制:遇到‘黄灯’就是追尾(环),遇到‘红灯’就是安全。”我们需要区分三种状态,防止重复计算并精准定位“回边”。

  • 0 (White/未访问):处女地,没去过。
  • 1 (Gray/正在访问):当前递归栈中的节点(黄灯)。如果在递归中又遇到了 1,说明咬到了自己的尾巴 -> 有环!
  • 2 (Black/已完成):死胡同或已经确认安全的节点(红灯)。下次遇到直接跳过,不用再跑一遍。
2. 算法流程 (三步走)
  1. 建图 (Adjacency List):把[课程, 前置]转化为前置 -> 课程的邻接表。
  2. 遍历所有节点:图可能不是连通的,所以外层要用for循环遍历每一个节点。
  3. DFS (三色状态流转):
    • 进门:标记为1(正在经手)。
    • 查邻居:
      • 邻居是1:报警!有环!
      • 邻居是0:递归进去查。
      • 邻居是2:安全的,无视。
    • 出门:标记为2(封存归档),表示从我出发的所有路都走通了且没环。
🔍 代码回忆清单 (带注释版)

✅ 举例:

假设prerequisites = [[1,0], [2,0], [3,1]]

循环过程:

  1. p = [1, 0]→g[0].add(1)→g[0] = [1]
  2. p = [2, 0]→g[0].add(2)→g[0] = [1, 2]
  3. p = [3, 1]→g[1].add(3)→g[1] = [3]

最终邻接表:

g[0] = [1, 2] // 学完 0 可以学 1 或 2 g[1] = [3] // 学完 1 可以学 3 g[2] = [] // 学完 2 没有后续课程 g[3] = [] // 学完 3 没有后续课程
// 题目:LC 207. Course Schedule class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { // 1. 建图 (邻接表) //创建一个数组,每个元素是一个列表 List<Integer>[] g = new ArrayList[numCourses]; //:给每个位置初始化一个空列表 Arrays.setAll(g, i -> new ArrayList<>()); for (int[] p : prerequisites) { // p[1] 是先修课,p[0] 是后修课 // 边方向:先修 -> 后修 // 从先修课 p[1] 指向后修课 p[0] g[p[1]].add(p[0]); } // 2. 状态数组 (0:未访, 1:递归中, 2:已完成) int[] colors = new int[numCourses]; // 3. 处理非连通图 (孤岛也要查) for (int i = 0; i < numCourses; i++) { // 只有没访问过的才进 DFS // 如果 dfs 返回 true,说明"找到了环",也就是"无法完成" -> return false if (colors[i] == 0 && dfs(i, g, colors)) { return false; } } return true; // 全跑完了没发现环 } // 返回值:true 表示“发现了环” private boolean dfs(int x, List<Integer>[] g, int[] colors) { colors[x] = 1; // 标记:我正在这条路径上 (入栈) // g[x] 是学完 x 后可以学的课程列表。 // y 就是其中一个后续课程。 for (int y : g[x]) { // 核心判断逻辑 if (colors[y] == 1) { return true; // 撞见自己人/祖先了 -> 环! } if (colors[y] == 0 && dfs(y, g, colors)) { return true; // 递归子节点发现了环,层层上报 } // colors[y] == 2 的情况直接跳过 (剪枝) } colors[x] = 2; // 标记:我和我的子孙都检查过了,没问题 (出栈) return false; // 平安无事 } }
⚡ 快速复习 CheckList (易错点)
  • [ ]为什么不能只用 boolean visited?
    • 普通的visited只能分“去过”和“没去过”。
    • 我们需要区分:是“当前递归路径里遇到过 (环)”还是“别的路径已经验证过是安全的 (剪枝)”。这就是状态1和2的区别。
  • [ ]返回值逻辑绕晕了?
    • dfs返回true=有坏人 (有环)。
    • 主函数if (dfs(...))成立,说明有环,课程表没法修完,所以主函数返回false。
    • 负负得正的逻辑要理清。
  • [ ]边的方向?
    • 通常题目给[A, B]表示修 A 必须先修 B。
    • 建图最好是B -> A(学了 B 才能学 A)。这样如果有环,逻辑依然成立。
🖼️ 数字演练

假设依赖:0 -> 1,1 -> 0(互为前置,死循环)。

  1. DFS(0):
    • Colors[0] = 1。
    • 看邻居:1。
    • 1是颜色0-> 递归DFS(1)。
  1. DFS(1):
    • Colors[1] = 1。
    • 看邻居:0。
    • 关键点:0的颜色是1!(说明 0 还在递归栈里,还没有变绿)。
    • Return True(发现环)。
  1. Back to DFS(0):
    • 收到子递归的True。
    • Return True。
  1. Main:
    • 收到True(有环)。
    • Return False(不能完成)。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/11 7:42:53

提示工程架构师必知:提升粘性的8个技术手段

提示工程架构师必知&#xff1a;提升粘性的8个技术手段 引言&#xff1a;为什么“粘性”是提示工程的生死线&#xff1f; 你是否遇到过这样的场景&#xff1f; 公司花了几个月开发的AI提示系统&#xff0c;上线时用户蜂拥而至&#xff0c;可没过两周&#xff0c;使用率就暴跌至…

作者头像 李华
网站建设 2026/10/8 15:41:33

优化大数据领域HBase的内存使用

优化大数据领域HBase的内存使用关键词&#xff1a;大数据、HBase、内存使用优化、RegionServer、MemStore摘要&#xff1a;本文聚焦于大数据领域中HBase内存使用的优化问题。HBase作为一种分布式、面向列的开源数据库&#xff0c;在大数据存储和处理中有着广泛应用。然而&#…

作者头像 李华
网站建设 2026/10/8 17:00:13

AI原生应用个性化定制,优化业务流程

AI原生应用个性化定制&#xff0c;优化业务流程&#xff1a;让企业像“私人管家”一样聪明工作关键词&#xff1a;AI原生应用、个性化定制、业务流程优化、用户画像、智能决策、低代码开发、自动化工具摘要&#xff1a;本文将带您走进“AI原生应用”的世界&#xff0c;揭秘如何…

作者头像 李华
网站建设 2026/10/9 1:36:46

提示工程:技巧、方法与未来发展

原文&#xff1a;towardsdatascience.com/prompt-engineering-tips-approaches-and-future-directions-d6baf5f12285?sourcecollection_archive---------1-----------------------#2024-06-27 https://towardsdatascience.medium.com/?sourcepost_page---byline--d6baf5f1228…

作者头像 李华
网站建设 2026/10/9 1:44:46

大数据日志数据的存储架构设计思路

大数据日志数据的存储架构设计思路关键词&#xff1a;大数据日志、存储架构、分布式系统、数据采集、数据存储、数据处理、数据查询摘要&#xff1a;本文深入探讨了大数据环境下日志数据的存储架构设计思路。我们将从日志数据的特性出发&#xff0c;分析存储架构的核心需求&…

作者头像 李华