LeetCodehot 力扣热题100 课程表
题目背景
这个问题要求我们判断是否可以完成所有课程的学习。每门课程可能依赖其他课程作为前置课程,构成了一个有向图。我们需要确定是否存在环,若存在环,说明课程之间互相依赖,无法完成所有课程;如果不存在环,则说明可以完成所有课程。
思路解析
1. 图的构建
我们将课程之间的依赖关系视为一个有向图:
• 课程是图的节点。
• 前置课程之间的依赖关系是有向边。例如,如果课程 A 需要课程 B,则我们在图中添加一条从 B 到 A 的边。
2. DFS 检测环
要检查图中是否存在环,经典的方法是使用 深度优先搜索(DFS):
• 正在访问的节点(颜色为 1):表示该节点正在递归访问中,遇到这种节点意味着图中有环。
• 已访问的节点(颜色为 2):表示该节点及其所有后续节点已经被访问过,且没有发现环。
• 未访问的节点(颜色为 0):表示该节点尚未访问。
DFS 的步骤:
• 遍历每个课程,如果课程未访问过,就进行 DFS 检查。
• 如果在 DFS 过程中,发现已经有课程正在访问(即发现环),则返回 false。
• 如果所有课程都能够成功访问且没有发现环,返回 true。
3. 终止条件
• 如果 DFS 中发现环,直接返回 false,说明存在循环依赖。
• 如果 DFS 完成后没有发现环,则返回 true,说明可以完成所有课程。
具体运行步骤
假设有以下示例输入:
int numCourses = 4;
vector<vector<int>> prerequisites = {{1, 0}, {2, 1}, {3, 2}};
这表示有 4 门课程,依赖关系如下:
• 课程 1 依赖课程 0
• 课程 2 依赖课程 1
• 课程 3 依赖课程 2
步骤 1:图的构建
首先,我们构建一个图,表示课程的依赖关系:
• g[0] = [1]:课程 0 依赖课程 1
• g[1] = [2]:课程 1 依赖课程 2
• g[2] = [3]:课程 2 依赖课程 3
• g[3] = []:课程 3 没有任何依赖关系
vector<vector<int>> g(numCourses);
for (auto& p : prerequisites) {
g[p[1]].push_back(p[0]);
}
步骤 2:DFS 遍历图
接下来,我们使用 DFS 遍历图来检查是否存在环。我们使用一个 colors 数组来标记每个课程的状态:
• colors[i] == 0:课程 i 尚未访问
• colors[i] == 1:课程 i 正在访问中(在当前递归栈中)
• colors[i] == 2:课程 i 已访问完毕
对于每个课程,如果它尚未访问,则启动 DFS:
for (int i = 0; i < numCourses; i++) {
if (colors[i] == 0 && dfs(i, g, colors)) {
return false; // 如果发现环,返回 false
}
}
步骤 3:DFS 检查环
在 dfs 函数中,我们递归访问课程的前置课程。如果发现正在访问的课程,说明存在环;如果课程已经完全访问过,则跳过。
bool dfs(int x, vector<vector<int>>& g, vector<int>& colors) {
colors[x] = 1; // 当前课程 x 正在访问中
for (int y : g[x]) { // 遍历课程 x 的所有前置课程 y
if (colors[y] == 1 || (colors[y] == 0 && dfs(y, g, colors))) {
return true; // 如果发现了环:1) y 正在访问中;2) y 尚未访问且递归时发现环
}
}
colors[x] = 2; // 完全访问完课程 x
return false; // 没有发现环
}
代码实现
具体运行步骤
以 numCourses = 4 和 prerequisites = {{1, 0}, {2, 1}, {3, 2}} 为例,具体的运行步骤如下:
1. 构建图:
• g[0] = [1]
• g[1] = [2]
• g[2] = [3]
• g[3] = []
2. DFS 遍历图:
• 从课程 0 开始,检查它的前置课程,发现课程 1,继续递归。
• 课程 1 的前置课程是课程 2,继续递归。
• 课程 2 的前置课程是课程 3,继续递归。
• 课程 3 没有前置课程,返回并标记为已访问。
• 完成所有课程的遍历,发现没有环。
3. 返回 true:
• 所有课程都没有形成环,因此返回 true,说明可以完成所有课程。
时间复杂度
• 图构建:时间复杂度为 O(E),其中 E 是 prerequisites 数组中的边数。
• DFS 遍历:每个课程和每条依赖边只会被访问一次,因此 DFS 的时间复杂度是 O(V + E),其中 V 是课程数,E 是依赖关系数。
因此,总体时间复杂度是 O(V + E),其中 V 是课程数,E 是依赖关系数。
好的,下面我会为你提供更加详细的运行步骤,并结合一个具体的示例进行演示,帮助你理解如何通过图的 DFS(深度优先搜索)来检测是否能完成所有课程。
题目示例
假设我们有 4 门课程,依赖关系为:
numCourses = 4;
prerequisites = {{1, 0}, {2, 1}, {3, 2}};
这表示:
• 课程 1 依赖于课程 0
• 课程 2 依赖于课程 1
• 课程 3 依赖于课程 2
目标是确定是否能够完成所有课程。如果有环(即课程之间存在循环依赖),则返回 false,否则返回 true。
步骤 1:构建图
首先,我们需要构建图。课程的依赖关系被表示为一个有向图,每个节点代表一门课程,每条边表示一门课程的前置课程。
对于 prerequisites = {{1, 0}, {2, 1}, {3, 2}},图将如下构建:
• 课程 0 -> 课程 1 (课程 1 依赖课程 0)
• 课程 1 -> 课程 2 (课程 2 依赖课程 1)
• 课程 2 -> 课程 3 (课程 3 依赖课程 2)
最终图的结构是:
g[0] = [1] // 课程 0 -> 课程 1
g[1] = [2] // 课程 1 -> 课程 2
g[2] = [3] // 课程 2 -> 课程 3
g[3] = [] // 课程 3 没有依赖
步骤 2:DFS 遍历图
接下来,我们将使用 深度优先搜索(DFS) 来遍历图,并检测是否存在环。我们需要使用一个 colors 数组来标记课程的访问状态:
• 0 表示该课程尚未访问。
• 1 表示该课程正在访问中(即当前递归栈中的课程)。
• 2 表示该课程已完全访问过。
步骤 3:进行 DFS 遍历
初始化
1. 初始化 colors 数组:
vector<int> colors(numCourses, 0); // 初始值全为 0,表示所有课程都未访问
2. 调用 dfs(i) 来访问每个课程。如果在 DFS 中发现环,则返回 false;否则,返回 true。
具体的 DFS 过程
1. 从课程 0 开始进行 DFS:
• colors[0] 设置为 1,表示课程 0 正在访问中。
• 课程 0 依赖于课程 1,递归调用 dfs(1)。
2. 在 dfs(1) 中:
• colors[1] 设置为 1,表示课程 1 正在访问中。
• 课程 1 依赖于课程 2,递归调用 dfs(2)。
3. 在 dfs(2) 中:
• colors[2] 设置为 1,表示课程 2 正在访问中。
• 课程 2 依赖于课程 3,递归调用 dfs(3)。
4. 在 dfs(3) 中:
• colors[3] 设置为 1,表示课程 3 正在访问中。
• 课程 3 没有依赖课程,所以直接将 colors[3] 设置为 2,表示课程 3 完成访问。
5. 返回到 dfs(2):
• 课程 2 完成访问,colors[2] 设置为 2。
• 返回到 dfs(1)。
6. 返回到 dfs(0):
• 课程 0 完成访问,colors[0] 设置为 2。
• 所有课程都已完成访问。
步骤 4:结果判断
在整个 DFS 遍历过程中,如果没有发现环(即没有遇到 colors[x] == 1 的情况),则说明所有课程都可以完成。
最终,colors 数组的状态为:
colors = {2, 2, 2, 2}
所有课程的状态都为 2,表示它们都已完成访问,没有发现环,因此可以完成所有课程,返回 true。
相关文章:
LeetCodehot 力扣热题100 课程表
题目背景 这个问题要求我们判断是否可以完成所有课程的学习。每门课程可能依赖其他课程作为前置课程,构成了一个有向图。我们需要确定是否存在环,若存在环,说明课程之间互相依赖,无法完成所有课程;如果不存在环&#x…...
彻底卸载kubeadm安装的k8s集群
目录 一、删除资源 二、停止k8s服务 三、重置集群 四、卸载k8s安装包 五、清理残留文件和目录 六、删除k8s相关镜像 七、重启服务器 一、删除资源 # 删除集群中的所有资源,包括 Pod、Deployment、Service,任意节点执行 kubectl delete --all pod…...
【HarmonyOS Next】拒绝权限二次申请授权处理
【HarmonyOS Next】拒绝权限二次申请授权处理 一、问题背景: 在鸿蒙系统中,对于用户权限的申请,会有三种用户选择方式: 1.单次使用允许 2.使用应用期间(长时)允许 3.不允许 当用户选择不允许后࿰…...
便携式动平衡仪Qt应用层详细设计方案(基于Qt Widgets)
便携式动平衡仪Qt应用层详细设计方案(基于Qt Widgets) 版本:1.0 日期:2023年10月 一、系统概述 1.1 功能需求 开机流程:长按电源键启动,全屏显示商标动画(快闪3~4次)。主界面&…...
2 20 数据开发面试题汇总
下面是我在实习中协助面试 然后在牛客上挑选了一些完整的面试问题借助豆包完成的面经答案思路汇总 滴滴数据研发日常实习 一面 数据仓库认识维度建模之外还有哪些建模,有什么区别项目中数据仓库分了哪几层,为什么要分层Hadoop架构,你这些组…...
跟着李沐老师学习深度学习(十四)
注意力机制(Attention) 引入 心理学角度 动物需要在复杂环境下有效关注值得注意的点心理学框架:人类根据随意线索和不随意线索选择注意力 注意力机制 之前所涉及到的卷积、全连接、池化层都只考虑不随意线索而注意力机制则显示的考虑随意…...
Websocket——心跳检测
1. 前言:为什么需要心跳机制? 在现代的实时网络应用中,保持客户端和服务端的连接稳定性是非常重要的。尤其是在长时间的网络连接中,存在一些异常情况,导致服务端无法及时感知到客户端的断开,可能造成不必要…...
基于YOLO11深度学习的半导体芯片缺陷检测系统【python源码+Pyqt5界面+数据集+训练代码】
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...
Spring Boot3.x集成Flowable7.x(一)Spring Boot集成与设计、部署、发起、完成简单流程
一、Flowable简介 Flowable 是一个轻量级、开源的业务流程管理(BPM)和工作流引擎,旨在帮助开发者和企业实现业务流程的自动化。它支持 BPMN 2.0 标准,适用于各种规模的企业和项目。Flowable 的核心功能包括流程定义、流程执行、任…...
Dify 工作流分类器技巧
在使用 Dify 工作流中的分类器(如问题分类器,Question Classifier)时,想要实现高效且准确的分类,可以遵循以下技巧和最佳实践。这些建议基于 Dify 的工作流功能,帮助你更好地设计和优化分类过程:…...
网络安全-openssl工具
OpenSSl是一个开源项目,包括密码库和SSL/TLS工具集。它已是在安全领域的事实标准,并且拥有比较长的历史,现在几乎所有的服务器软件和很多客户端都在使用openssl,其中基于命令行的工具是进行加密、证书管理以及测试最常用到的软件。…...
【Web开发】PythonAnyWhere免费部署Django项目
PythonAnyWhere免费部署Django项目 文章目录 PythonAnyWhere免费部署Django项目将项目上传到GitHub从GitHub下载Django项目创建Web应用配置静态文件将项目上传到GitHub 打开项目,输入以下命令,生成Django项目依赖包。pip list --format=freeze > requirements.txt打开Git …...
Spring Boot 多模块怎么统一管理
在 Spring Boot 中,多模块项目是一种常见的架构模式,尤其适用于构建大型、复杂的应用程序。将应用程序拆分成多个模块可以提高代码的可维护性、可重用性和团队协作效率。然而,多模块项目也带来了一些管理上的挑战,例如依赖版本管理…...
视频的分片上传
分片上传需求分析: 项目中很多地方需要上传视频,如果视频很大,上传到服务器需要很多时间 ,这个时候体验就会很差。所以需要前端实现分片上传的功能。 要实现分片上传,需要对视频进行分割,分割成不同的大小…...
Moonshot AI 新突破:MoBA 为大语言模型长文本处理提效论文速读
前言 在自然语言处理领域,随着大语言模型(LLMs)不断拓展其阅读、理解和生成文本的能力,如何高效处理长文本成为一项关键挑战。近日,Moonshot AI Research 联合清华大学、浙江大学的研究人员提出了一种创新方法 —— 混…...
Deepseek首页实现 HTML
人工智能与未来:机遇与挑战 引言 在过去的几十年里,人工智能(AI)技术取得了突飞猛进的发展。从语音助手到自动驾驶汽车,AI 正在深刻地改变我们的生活方式、工作方式以及社会结构。然而,随着 AI 技术的普及…...
AI革命下的多元生态:DeepSeek、ChatGPT、XAI、文心一言与通义千问的行业渗透与场景重构
前言 人工智能技术的爆发式发展催生了多样化的AI模型生态,从通用对话到垂直领域应用,从数据挖掘到创意生成,各模型凭借其独特的技术优势与场景适配性,正在重塑全球产业格局。本文将以DeepSeek、ChatGPT、XAI(可解释人…...
VS2022配置FFMPEG库基础教程
1 简介 1.1 起源与发展历程 FFmpeg诞生于2000年,由法国工程师Fabrice Bellard主导开发,其名称源自"Fast Forward MPEG",初期定位为多媒体编解码工具。2004年后由Michael Niedermayer接任维护,逐步发展成为包含音视频采…...
kafka基本知识
什么是 Kafka? Apache Kafka 是一个开源的分布式流处理平台,最初由 LinkedIn 开发,后来成为 Apache 软件基金会的一部分。Kafka 主要用于构建实时数据管道和流处理应用程序。它能够高效地处理大量的数据流,广泛应用于日志收集、数…...
类型系统下的语言分类与类型系统基础
类型系统是一种根据计算值的种类对程序语法进行分类的方式,目的是自动检查是否有可能导致错误的行为。 —Benjamin.C.Pierce,《类型与编程语言》(2002) 每当谈到编程语言时,人们常常会提到“静态类型”和“动态类型”。…...
力扣-回溯-93 复原IP地址
思路 用一个vector存放可能的结果,然后用一个变量判断插入点的数量,假设再最后一段后也插入点 代码 class Solution { public:vector<string> result;vector<string> path;int toNum(string s){int d 1;int result 0;for(int i s.size…...
SpringSecurity设置白名单
Spring Security 访问权限系列文章: 《SpringSecurity基于配置方法控制访问权限:MVC匹配器、Ant匹配器》 《SpringSecurity基于注解实现方法级别授权:PreAuthorize、PostAuthorize、Secured》 《SpringSecurity设置白名单》 白名单࿰…...
有没有使用wxpython开发的类似于visio或drawio的开源项目(AI生成)
有没有使用wxpython开发的类似于visio或drawio的开源项目 是的,有一些使用wxPython开发的类似于Microsoft Visio或draw.io(现为diagrams.net)的开源项目。wxPython 是一个跨平台的GUI工具包,它允许Python开发者创建桌面应用程序&…...
HTML之JavaScript DOM操作元素(2)
HTML之JavaScript DOM操作元素(2) 4.增删元素var element document.createElement("元素名") 创建新元素父元素.appendChild(子元素) 在父元素中追加子元素父元素.insertBefore(新元素,参照元素) 在特定元素之前新增元…...
前端八股——JS+ES6
前端八股:JSES6 说明:个人总结,用于个人复习回顾,将持续改正创作,已在语雀公开,欢迎评论改正。...
day58 第十一章:图论part08
拓扑排序精讲 关键: 先找到入度为0的节点,把这些节点加入队列/结果,然后依次循环再找。 #include <iostream> #include <vector> #include <queue> #include <unordered_map> using namespace std; int main() {int …...
【MySQL 一 数据库基础】深入解析 MySQL 的索引(3)
索引 索引操作 自动创建 当我们为一张表加主键约束(Primary key),外键约束(Foreign Key),唯一约束(Unique)时,MySQL会为对应的的列自动创建一个索引;如果表不指定任何约束时,MySQL会自动为每一列生成一个索引并用ROW_I…...
【C++】优先级队列宝藏岛
> 🍃 本系列为初阶C的内容,如果感兴趣,欢迎订阅🚩 > 🎊个人主页:[小编的个人主页])小编的个人主页 > 🎀 🎉欢迎大家点赞👍收藏⭐文章 > ✌️ 🤞 …...
解决elementUi el-select 响应式不生效的问题
情况一,字段类型不匹配 考虑option的value值的字段类型是否和api返回的字段类型一致,如果一个为字符串一个为数字类型是无法匹配上的 <template> <div><el-select v-model"value" size"large"style"width: 240px"&…...
List 接口中的 sort 和 forEach 方法
List 接口中的 sort 和 forEach 方法是 Java 8 引入的两个非常实用的函数,分别用于 排序 和 遍历 列表中的元素。以下是它们的详细介绍和用法: sort 函数 功能 对列表中的元素进行排序。 默认使用自然顺序(如数字从小到大,字符…...
