207. 课程表

一、题目描述

你这个学期必须要学习numCourses门课程,记录为0到numCourses-1。在选修某些课程之前先修课程。例如,要学习课程0,必须先学习课程1,我们用一个匹配表示[0,1]。

输入:2,[[1,0]]
输出:true
解释:总共有2个课程,为了学习课程1,必须先学习课程0,所以这是可能的。

二、解答

课程 0 需要先修课程 1
课程 1 需要先修课程 2
课程 2 需要先修课程 3
  • 需要一个数组inDegree,表示入度数据
  • int[][] graph 表示课程表的图

1、构建图和计算入度

  • 初始化每个课程的入度为0:inDegree = [0,0,0,0]
  • 构建图,表示课程之间的依赖关系
课程 1 的列表添加课程 0
课程 2 的列表添加课程 1
课程 3 的列表添加课程 2

图表示为列表: graph = [[],[0],[1],[2]]
graph的索引代表课程编号,而每个索引对应的列表包含了依赖这个课程的后续课程。下标可以理解为学习课程的起点,数组内容就是后续要修的课程。
具体到这个例子中的graph = [[], [0], [1], [2]]
(1)graph[0] 是一个空列表,表示没有任何课程依赖课程0.
(2)graph[1] 是包含[0],表示课程1是课程0的先修课程,也就是说,你必须先完成课程1才能学习课程0
(3)graph[2]包含[1],表示课程2是课程1的先修课程,同样地,必须要完成课程2才能学习课程1。
(4)graph[3]包含[2],表示课程3是课程2的先修课程,你必须完成课程3才能学习课程2。

这个表直观展示了每个课程对哪些后续课程的先修条件。当我们在拓扑排序过程中选择一个课程时候,我们可以快速查找依赖它的课程,并对它们的入度进行更新。
例如,当我们从队列中取出课程3时候,我们查看graph[3],我们得知课程2依赖课程3,所以我们将课程2的入度减去1,如果课程2的入度为0,那么我将课程2加入队列,因为没有其他课程阻碍它了,它现在可以学习了。这个过程会一直重复,一直到所有课程的入度都被处理完毕。如果最终所有的课程入度减少到了0,那么我们就可以完成所有课程的学习。

  • 更新每个课程的入度
课程 0 的入度为 1
课程 1 的入度为 1
课程 2 的入度为 1
课程 3 的入度为 0

更新后的入度: inDegree = [1, 1, 1, 0]。

2、执行BFS
(1)将所有的入度为0的课程加入队列:queue = [3]。
(2)初始化已完成的课程数量: count = 0。
3、开始排序
(1)从队列中取出课程3,将count加1,现在count加1,现在count = 1。
(2)检查课程3的邻接课程(依赖课程3的课程),这里是课程2。
(3)将课程2的入度减1,变为0,然后将课程2加入队列:queue = [2]。
4、继续BFS
(1)从队列中取出课程2,将count加1,现在count = 2。
(2)检查课程2的邻接课程,这里是课程1。
(3)将课程1的入度减1,变为0,然后将课程1加入队列:queue = [1]。
5、重复上述步骤
(1)从队列中取出课程1,将count加1,现在count = 3.
(2)检查课程1的邻接课程,这里是课程0。
(3)将课程9的入度减1,变为0,然后将课程0加入队列:queue = [0]。
6、最后一步
(1)从队列中取出课程0,将count加1,现在count = 4。
(2)课程0没有邻接课程,队列变为空。
7、检查结果
(1)最后 count = 4,等于课程总数 numCourses = 4。
(2)由于我们能够完成所有课程的学习,所以算法返回 true。

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] inDegree = new int[numCourses];
        Map<Integer, List<Integer>> graph = new HashMap<>();
        
        // 构建图并计算入度
        for (int[] prerequisite : prerequisites) {
            int course = prerequisite[0];
            int preCourse = prerequisite[1];
            inDegree[course]++;
            
            if (!graph.containsKey(preCourse)) {
                graph.put(preCourse, new ArrayList<>());
            }
            graph.get(preCourse).add(course);
        }
        
        // 将所有入度为 0 的课程加入队列
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) {
                queue.add(i);
            }
        }
        
        int count = 0;
        // BFS
        while (!queue.isEmpty()) {
            int preCourse = queue.poll();
            count++;
            
            // 获取所有依赖 preCourse 的课程并更新它们的入度
            if (graph.containsKey(preCourse)) {
                for (int course : graph.get(preCourse)) {
                    inDegree[course]--;
                    if (inDegree[course] == 0) {
                        queue.add(course);
                    }
                }
            }
        }
        
        return count == numCourses;
    }
}

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值