news 2026/3/21 21:02:00

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

作者头像

张小明

前端开发工程师

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/3/16 16:25:04

如何用OpCore Simplify智能工具高效构建黑苹果EFI配置

如何用OpCore Simplify智能工具高效构建黑苹果EFI配置 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify OpCore Simplify是一款开源智能工具&#xff0c…

作者头像 李华
网站建设 2026/3/14 20:19:45

解锁Obsidian插件本地化:探索多语言界面配置的创新方案

解锁Obsidian插件本地化&#xff1a;探索多语言界面配置的创新方案 【免费下载链接】obsidian-i18n 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-i18n Obsidian作为一款强大的知识管理工具&#xff0c;其丰富的插件生态极大扩展了功能边界。然而&#xff0c…

作者头像 李华
网站建设 2026/3/19 23:02:22

亲测unet person image cartoon compound镜像,效果惊艳的AI卡通生成体验

亲测unet person image cartoon compound镜像&#xff0c;效果惊艳的AI卡通生成体验 1. 开箱即用&#xff1a;从启动到第一张卡通图只要3分钟 第一次打开这个镜像时&#xff0c;我特意掐了表——从执行启动命令到看到网页界面&#xff0c;再到上传照片、调整参数、点击转换&a…

作者头像 李华
网站建设 2026/3/13 7:03:23

显存只有8GB也能行!麦橘超然让Flux模型轻松落地

显存只有8GB也能行&#xff01;麦橘超然让Flux模型轻松落地 1. 为什么8GB显存用户终于能用上Flux了&#xff1f; 你是不是也经历过这样的尴尬&#xff1a;看到Flux.1生成的图片惊艳得想立刻试试&#xff0c;结果一查显存要求——“推荐24GB VRAM”&#xff0c;默默关掉了网页…

作者头像 李华
网站建设 2026/3/14 0:04:30

YOLO26镜像避坑指南:从环境配置到模型训练全流程解析

YOLO26镜像避坑指南&#xff1a;从环境配置到模型训练全流程解析 在目标检测领域&#xff0c;YOLO系列始终以“快、准、稳”著称。随着技术演进&#xff0c;最新发布的 YOLO26 在架构设计和任务统一性上实现了进一步突破&#xff0c;不仅支持目标检测&#xff0c;还无缝集成实…

作者头像 李华
网站建设 2026/3/18 14:57:42

微信联系科哥获取支持:fft npainting lama使用答疑

微信联系科哥获取支持&#xff1a;fft npainting lama使用答疑 1. 快速上手图像修复系统 1.1 启动服务与访问界面 如果你已经部署了“fft npainting lama重绘修复图片移除图片物品 二次开发构建by科哥”这个镜像&#xff0c;接下来就可以快速启动并使用它来处理图像。整个过…

作者头像 李华