207. 课程表

文章目录

题目

图论:207. 课程表

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

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisitesi = 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

prerequisitesi.length == 2

0 <= ai, bi < numCourses

prerequisitesi 中的所有课程对 互不相同

代码

cpp 复制代码
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
      vector<int>ingree(numCourses,0);//入度数组:记录每门课有多少先修课还没修
      unordered_map<int,vector<int>>preCourse;//邻接表:key=课程,value=学完这门课后可以解锁的后续课程
      for(int i=0;i<prerequisites.size();i++){
        // 前提:prerequisites[i][0] 想要上课,必须先上 prerequisites[i][1]
        // 所以建边:prerequisites[i][1] → prerequisites[i][0]
        preCourse[prerequisites[i][1]].push_back(prerequisites[i][0]);
        // 0号课程的入度 +1(因为它依赖1号课程)
        ingree[prerequisites[i][0]]++;
      }
      // BFS队列:存放入度为0(没有先修课,可以直接上)的课程
      queue<int>q;
    // 统计已经上完的课程数
      int count=0;
      //初始化队列:把所有入度为0的课放进去
      for(int i=0;i<numCourses;++i){
        if(!ingree[i]){
            q.push(i);
            ++count;//这门课可以直接上,计数+1
        }
      }
      while(!q.empty()){//队列不为空的前提下
        int cur=q.front();//取出队头
        q.pop();//删除队头
        //遍历这门课能解锁的所有后续课程
        for(int i=0;i<preCourse[cur].size();++i){
            --ingree[preCourse[cur][i]];//队头依赖的课程的入度减1
            // 如果入度变成0 → 没有先修课了,可以上了
            if(!ingree[preCourse[cur][i]]){//将度为0的课程压入队列
                q.push(preCourse[cur][i]);
                ++count;
            }
        }
      }
    if(count==numCourses)return true;
    return false;
    }
};

原理图

原理解释

提示:算法流程及解释在代码中已标注

判断有向图中是否存在环,如果存在环说明互相依赖为false

如果不存在环说明可以是实现学完所有课程为true

拓扑排序 + 广度优先搜索 (BFS)

1、初始化一个入度数组,记录每门课有多少先修课还没完成。

2、建立邻接表,表示课程之间的依赖关系:学完 A 可以解锁哪些课。

3、遍历所有先修条件,把依赖关系填入邻接表,并更新对应课程的入度。

4、遍历所有课程,把入度为 0(没有先修课、可以直接学)的课程加入队列。

5、用计数器记录已经修完的课程数量,初始为入度 0 的课程数。

6、开始 BFS 循环:取出队列头部的一门课,表示当前修完这门课。

7、遍历这门课能解锁的所有后续课程,将它们的入度减 1。

8、若某后续课程入度减到 0,说明它的先修课都修完了,加入队列并增加计数。

9、重复直到队列为空。

10、最后判断:修完的课程总数是否等于总课程数。

相等 → 无环,可以修完所有课 → 返回 true

不相等 → 有环,无法修完 → 返回 false

相关推荐
蹊黎2 小时前
C++中vector类的部分简单实现
开发语言·c++·visual studio
强哥聊算力4 小时前
《算力江湖·英伟达传奇》第七集:ChatGPT出现前夜,英伟达发生了什么?
服务器·人工智能·算法·ai·chatgpt·gpu算力
Navigator_Z8 小时前
LeetCode //C - 1289. Minimum Falling Path Sum II
c语言·算法·leetcode
CCblandy8 小时前
令牌桶在 0.9 倍负载超发 19%、固定窗口边界放行 20 个:四种限流算法同构实测
后端·python·算法·性能优化·架构
xier_ran9 小时前
【infra之路】AI 编译器学习笔记(六):重拾 C++——lambda 与 MLIR 中的回调
c++·笔记·学习
YangYang9YangYan10 小时前
2027供应链计划校招:数学与应用数学专业如何把预测模型讲成业务决策?
人工智能·sql·算法
jsjzsl210 小时前
递归自改进系统(RSI)的本质是自发对称破缺:智能爆炸有界—— AI研发切记盲目投入
人工智能·python·算法
程序员清风10 小时前
最短路径算法:BFS、Dijkstra 与 Floyd
java·算法·宽度优先
梦帮科技10 小时前
Kali Linux Scapy 畸形报文构造学——深度解剖非法 TCP Flags 与反常序列号
linux·爬虫·神经网络·算法·数学建模·系统架构·模拟退火算法