news 2026/4/18 7:05:54

《课程表危机》:如何用拓扑排序检测“循环依赖”?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《课程表危机》:如何用拓扑排序检测“循环依赖”?

题目背景:

小明这学期选了一堆课,但是他发现学校的选课系统有点坑。有些课程有前置要求,比如必须先修完“高等数学”,才能去修“大学物理”。

小明整理了一份课程依赖清单,但他隐约觉得这份清单有问题——如果存在 A 依赖 B,B 依赖 C,C 又依赖 A 的情况,那他就永远没法把课修完了(死循环)!

现在请你帮小明检查一下,按照这份清单,他到底能不能顺利修完所有课程?

题目描述:

学校里共有n门课程,编号从1到n。

小明拿到了m条先修规则。每一条规则描述为u v,表示课程u是课程v的先修课(即必须先修完 u,才能修v)。

请你判断是否存在“课程死锁”(即循环依赖),如果存在死锁,说明小明不能修完所有课;如果不存在死锁,说明他可以顺利修完。

输入格式:

第一行输入两个正整数n, m,分别表示课程总数和先修规则的数量。

接下来m行,每行两个整数u, v,表示课程u必须在课程v之前修完。

输出格式:

如果存在循环依赖(即无法修完),输出Yes

如果不存在循环依赖(即可以修完),输出No

样例输入:

4 4 1 2 2 3 3 4 1 4

样例输出:

No

样例解释:

课程依赖关系为:1->2, 2->3, 3->4, 1->4。

修课顺序可以是:1 -> 2 -> 3 -> 4。不存在环,所以输出No(代表没有死锁)。

数据范围:

2<=n<=10000

0<=m<=100000

1<=u, v<=n, u!=v

1. 背景故事:选课的烦恼

小明这学期选了一堆课,但是他发现学校的选课系统有点坑。有些课程有前置要求,比如必须先修完“高等数学”,才能去修“大学物理”。

小明整理了一份课程依赖清单,但他隐约觉得这份清单有问题——如果存在 A 依赖 B,B 依赖 C,C 又依赖 A 的情况,那他就永远没法把课修完了(死循环)!

作为计算机系的学生,我们能不能写个程序帮小明检查一下:按照这份清单,他到底能不能顺利修完所有课程?

2. 问题抽象

本质上,这是一个有向图的问题:

  • 节点:代表每一门课程。

  • 有向边:代表依赖关系。如果课程u是v的先修课,则存在一条边u->v。

  • 目标:判断图中是否存在

如果图中存在环(例如1->2->3->1),则说明存在循环依赖,无法修完;如果是一个有向无环图,则说明存在一个合理的修课顺序(即拓扑序列)。

3. 核心算法:拓扑排序 (Kahn算法)

要解决这个问题,最经典的方法是使用Kahn算法(基于入度的拓扑排序)。

算法步骤:

  1. 统计入度:计算每一门课有多少门“直接先修课”(入度)。

  2. 入队:找到所有入度为0的课(不需要先修课,直接能上的课),放入队列。

  3. 消除

    • 从队列取出一门课(假设修完了)。

    • 将这门课指向的所有后续课程的入度减1(相当于解锁了它们的一个条件)。

    • 如果某门后续课程的入度变成了0,说明它的条件全满足了,将其入队。

  4. 判断

    • 如果最终入过队的课程数量等于总课程数N,说明所有课都能修完(无环)。

    • 如果小于N,说明剩下的课互为前置,形成了环(有环)。

4. 代码实现

为了追求极致的运行效率,本代码采用了以下两个优化技巧,非常适合算法竞赛(ACM/OI):

  1. 链式前向星:替代vector存图,内存占用更小,遍历速度更快。

  2. 手写队列:替代queue,减少STL的常数开销。

题目描述

  • 输入:n (课程数), m (规则数)。接下来m行u, v,表示u是v的先修课。

  • 输出:如果有环(无法修完),输出Yes;否则输出No

完整代码

//有向图环判断 链式前向星+手写队列版本 #include <iostream> #include <cstring> using namespace std; int n,m; int h[10010];//邻接表头指针数组 int vtex[100010];//临接表第i条边终点下标 int nxt[100010];//与第i条边同起点的下一条边的索引 int idx;//边数 int d[10010];//存每个点的入度 int q[10010];//存入度为0的点(手写队列) void addedge(int u,int v){ vtex[idx]=v; nxt[idx]=h[u]; h[u]=idx++; } void tuopu(){ int rear=0; int front=1; //先把一开始入度为0的点(无需先修课)都存入队列q for(int i=1;i<=n;i++) if(d[i]==0) q[++rear]=i; while(front<=rear){//front=rear+1代表队列为空 int x=q[front];//访问队首元素 front++;//队首元素出队 //接下来把所有从x出发的边都删掉 然后这些边所指向的点的入度都减1 //x是p的先修课 int p=h[x]; while(p!=-1){//访问x所有邻接点 d[vtex[p]]--;//因为删除了和邻接点之间的边,所以入度都要减1 //如果邻接点入度变为0,就入队 if(d[vtex[p]]==0) q[++rear]=vtex[p]; p=nxt[p];//更新指针 } } if(rear==n) cout<<"No";//如果所有点都入队了,代表没有环 else cout<<"Yes";//否则就是有环 } int main() { cin>>n>>m; //初始化头指针数组为空 memset(h,-1,sizeof(h)); //邻接表存图 for(int i=1;i<=m;i++){ int u,v; cin>>u>>v; addedge(u,v); d[v]++;//v点入度+1 } //拓扑排序 tuopu(); return 0; }

5. 复杂度分析

  • 时间复杂度:O(N+M)。

    • 我们需要遍历所有的点(初始化入度)一次。

    • 在拓扑排序过程中,每条边会被遍历一次(d[v]--)。

    • 这是图论算法中线性的最高效率。

  • 空间复杂度:O(N+M)。

    • 使用了链式前向星存储图,空间与边数成正比。

6. 总结

拓扑排序是解决依赖关系解析的标准工具。无论是大学选课、软件安装包的依赖管理(如 pip, npm),还是工程项目的进度安排,其底层逻辑都是判断图是否为 DAG(有向无环图)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 15:32:40

unet person image cartoon compound能否做动画帧处理?视频应用试探

unet person image cartoon compound能否做动画帧处理&#xff1f;视频应用试探 1. 功能概述 unet person image cartoon compound人像卡通化工具&#xff0c;由科哥基于阿里达摩院ModelScope平台的DCT-Net模型构建&#xff0c;核心功能是将真实人物照片自动转换为风格统一的…

作者头像 李华
网站建设 2026/4/18 6:30:21

3步搞定!让黑苹果配置像组装宜家家具一样简单

3步搞定&#xff01;让黑苹果配置像组装宜家家具一样简单 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 你是否曾面对满屏的代码和驱动手足无措&…

作者头像 李华
网站建设 2026/4/10 5:51:25

高效捕获网页媒体资源:猫抓工具的全方位应用指南

高效捕获网页媒体资源&#xff1a;猫抓工具的全方位应用指南 【免费下载链接】cat-catch 猫抓 chrome资源嗅探扩展 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 猫抓&#xff08;Cat-Catch&#xff09;是一款专业的浏览器扩展&#xff0c;能够自动检测…

作者头像 李华
网站建设 2026/4/16 19:49:26

突破实时3D渲染瓶颈:XV3DGS-UEPlugin全攻略

突破实时3D渲染瓶颈&#xff1a;XV3DGS-UEPlugin全攻略 【免费下载链接】XV3DGS-UEPlugin 项目地址: https://gitcode.com/gh_mirrors/xv/XV3DGS-UEPlugin XV3DGS-UEPlugin是一款基于3D高斯模型技术的虚幻引擎5插件&#xff0c;能够直接从照片或视频重建高质量3D场景并…

作者头像 李华
网站建设 2026/3/24 5:57:35

软件工具高效使用全攻略:IDM试用期管理实用技巧

软件工具高效使用全攻略&#xff1a;IDM试用期管理实用技巧 【免费下载链接】IDM-Activation-Script IDM Activation & Trail Reset Script 项目地址: https://gitcode.com/gh_mirrors/id/IDM-Activation-Script 本文将系统介绍一款针对IDM下载管理器的实用工具&…

作者头像 李华
网站建设 2026/3/25 13:44:21

英文Prompt实现万物分割|sam3模型镜像快速上手教程

英文Prompt实现万物分割&#xff5c;sam3模型镜像快速上手教程 你有没有试过&#xff0c;只输入“一只橘猫坐在窗台上”&#xff0c;就自动把图中那只猫精准抠出来&#xff1f;不是靠画框、不是靠点选&#xff0c;而是靠一句话——这不再是科幻&#xff0c;而是 SAM3 模型正在…

作者头像 李华