当前位置: 首页 > news >正文

LeetCode 207. 课程表

题目描述

这个学期需要选修numCourses门课程,课程编号为0numCourses - 1

数组prerequisites表示课程之间的先修关系,其中prerequisites[i] = [ai, bi]表示:如果要学习课程ai,必须先学习课程bi

例如:

[0, 1]

表示想学习课程0,需要先完成课程1

要求判断是否可以完成所有课程。如果可以,返回true;否则返回false

初始思路

一开始想到的是把二维数组转成链表,然后判断链表里是否存在循环。

但这题不是普通链表问题。一个课程可能有多个后续课程,也可能被多个课程依赖,所以它本质上是一张有向图,不是一条链表。

如果课程之间存在环,就说明有些课程互相依赖,无法完成所有课程。

例如:

0 -> 1 1 -> 0

这就表示学习0之前要先学1,学习1之前又要先学0,形成了死循环。

解题思路

这题可以用 DFS 判断有向图中是否存在环。

先根据prerequisites建图:

g[p[1]].add(p[0]);

这里的方向是:

先修课 -> 后续课

也就是如果p = [a, b],表示学a之前必须先学b,所以建边b -> a

然后用三色标记记录每个课程的访问状态:

  • 0:未访问。
  • 1:正在访问。
  • 2:已经访问完成。

DFS 过程中,如果遇到一个状态为1的节点,说明这个节点还在当前递归路径上,又被重新访问到了,所以存在环。

如果存在环,就无法完成所有课程,返回false

为什么需要三色标记

这题不能只用一个简单的visited

因为有向图判环时,需要区分两种状态:

  • 这个点以前访问过,并且已经确认它后面的路径没有环。
  • 这个点正在当前 DFS 路径中,还没有退出递归。

只有遇到“正在访问”的点,才说明形成了环。

也就是代码里的:

if (color[y] == 1) { return true; }

当一个节点的所有后续节点都 DFS 完成后,要把它标记成2

color[x] = 2;

表示这个点已经检查完成,以后再遇到它就不用重复搜索。

易错点

1. 建图方向

prerequisites[i] = [ai, bi]的含义是:学ai之前要先学bi

所以边的方向应该是:

bi -> ai

对应代码:

g[p[1]].add(p[0]);

2. DFS 结束后要标记为完成

如果一个点搜索完没有发现环,要把它从1改成2

否则其他路径再次访问到它时,可能会误以为遇到了环。

3. 外层要遍历所有课程

图不一定是连通的。

有些课程可能和课程0完全不在一个连通块里,所以不能只从一个课程开始 DFS,而是要遍历所有课程:

for (int i = 0; i < numCourses; i++) { if (color[i] == 0 && dfs(i, g, color)) { return false; } }

代码实现

class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { List<Integer>[] g = new ArrayList[numCourses]; Arrays.setAll(g, i -> new ArrayList<>()); int[] color = new int[numCourses]; for (int[] p : prerequisites) { g[p[1]].add(p[0]); } for (int i = 0; i < numCourses; i++) { if (color[i] == 0 && dfs(i, g, color)) { return false; } } return true; } public boolean dfs(int x, List<Integer>[] g, int[] color) { color[x] = 1; for (int y : g[x]) { if (color[y] == 1 || color[y] == 0 && dfs(y, g, color)) { return true; } } color[x] = 2; return false; } }

复杂度分析

  • 时间复杂度:O(numCourses + prerequisites.length)。每个课程节点和每条先修边最多被访问一次。
  • 空间复杂度:O(numCourses + prerequisites.length)。邻接表需要存储所有边,递归栈和颜色数组最多需要O(numCourses)

复盘

这题的关键是把课程关系看成一张有向图,然后判断图里有没有环。

最开始想用链表判环,是因为抓住了“循环依赖”这个方向,但没有意识到课程关系不是一条链,而是可能一对多、多对一的图结构。

下次遇到类似题时,可以先检查三点:

  • 依赖关系能不能抽象成有向图。
  • 边方向是否是先修课 -> 后续课
  • DFS 判环时是否区分了未访问正在访问已完成三种状态。
http://www.jsqmd.com/news/1284261/

相关文章:

  • 常熟打井找哪家?2026年最新攻略:苏州市瑞溪泉水利工程有限公司凭实力说话 - 瑞溪泉水利
  • Zotero插件市场:一站式插件管理与安装体验的终极指南
  • 凡科杰建云的小程序、商城、门店和建站数据互通吗?
  • 基于行空板与RC车打造可编程智能小车:从硬件连接到Web遥控
  • AIGC检测技术:守护学术原创性的AI解决方案
  • 2026年多功能万年历应用推荐:黄历道历佛历、亲友提醒和个人历怎么选?附天乙日历App完整测评
  • 2026年苏州专业处理个人欠款纠纷律师大盘点 - 品牌排行榜
  • 消费级外骨骼三条技术路线:适老、户外与轻量化如何选择?
  • K8s资源调度与HPA自动扩缩容!AI流量波峰波谷自动适配,实现Agent服务智能弹性伸缩、降本增效
  • 行空板M10集成百度与讯飞双语音引擎:嵌入式AI语音交互实践
  • 2026年,西宁地区如何选择可靠的竹床漏粪板合作品牌? - 装修教育财税推荐2026
  • Unity字体优化:基于TextMeshPro的自动化字符集扫描与精简方案
  • 生物3D打印核心技术解析:从生物墨水、打印技术到产业化挑战
  • MetaERP 与 Oracle EBS 在 COA(会计科目段)上是同源同构、哲学一致、逻辑相同,只是 MetaERP 把 EBS 的 “弹性域 + 多账簿” 做了云原生与元数据化升级,叫法更现代、
  • 建站平台怎么判断后期维护成本?
  • 2026 年 7 月新发布:英吉沙优秀的玻璃钢化工管道供应厂家哪个好,化工厂里藏着的这款耐腐“黑马”,居然能省出百万运维成本? - 行业推荐官【官方】
  • 龙岩出发西藏年度口碑榜:两款纯玩产品怎么选?这份避坑指南让你少花冤枉钱| 附:旅行社电话 - 西藏康泰旅行社
  • 如何5分钟快速部署Sunshine游戏串流服务器:打造家庭云游戏终极解决方案
  • 167、NPU的编译器开发:条件执行与分支处理
  • 尤克里里魔改指南:从玩具到乐器的全面升级与声学优化
  • 2026年苏州最擅长处理欠款纠纷的律师推荐 - 品牌排行榜
  • C++模板编程:从基础语法到实战应用,掌握泛型编程核心技能
  • 2026合肥零基础成人学历提升正规机构汇总 - 品牌排行榜
  • Flutter+OpenHarmony实现高性能分布式步骤条
  • Grok对话AI本地部署与API集成实战指南
  • (2026最新)绍兴本地人必选的靠谱漏水检测维修推荐:正规防水补漏防水-卫生间/厨房/屋顶/阳台/外墙渗漏水精准测漏,本地人的信赖之选 - 安佳防水
  • 2026年Turnitin AI检测达标指南:Turnitin AI率超标4.8元完整处理方案
  • 物联网安全:SE050安全元件与PIC32MX硬件适配实践
  • (2026最新)荆州本地人必选的靠谱漏水检测维修推荐:正规防水补漏防水-卫生间/厨房/屋顶/阳台/外墙渗漏水精准测漏,本地人的信赖之选 - 安佳防水
  • # 鸿蒙 HarmonyOS 应用开发实战(第26期)|石头剪刀布(Rock-Paper-Scissors)— 游戏逻辑与胜负判定精讲