题目链接
课程表
题目描述
注意点
- prerequisites[i] 中的所有课程对 互不相同
- prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi
- numCourses表示这个学期必须选修 numCourses 门课程
- prerequisites[i].length == 2
解答思路
- 最初想到的是使用一个数据结构存储每门课程索要学习的前置课程(很容易想到用Map进行存储),再使用一个数组存储当前哪些课是可以学习的(最初只有无前置课程的可以学习),随后不断根据这两个数据结构推出后续可以学习的课程,最终判断是否numCourses门课程都能学习,关键要注意循环终止条件。但是时间复杂度和空间复杂度都不理想,且这种方法也较为复杂
- 参照题解使用拓扑排序的思想解决本题,首先要了解入度和出度的概念:入度是学习某个课程前需要学习的所有课程,出度是学习了某个课程后对哪些课程有影响。随后以广度优先遍历的思想不断判断哪些课程可以学习,最终判断是否所有课程都能学习
- 广度优先遍历判断课程可以学习的过程是:每次遍历判断所有入度为0的课程,此时学习该课程无需前置课程或者前置课程都已学习完,任意课程确定能学习后,根据其出度将其影响的相应课程的入度减1,不断重复此过程直到没有新的课程可以学习为止
代码
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int res = 0;
// 存储学习当前课程的入度数量
int[] currCourses = new int[numCourses];
// 将该课程作为前置课程的相关课程列表
List<List<Integer>> prevCourseList = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
prevCourseList.add(new ArrayList<Integer>());
}
// 当前已学习课程
for (int i = 0; i < prerequisites.length; i++) {
Integer course1 = prerequisites[i][0];
Integer course2 = prerequisites[i][1];
currCourses[course1] += 1;
prevCourseList.get(course2).add(course1);
}
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < currCourses.length; i++) {
if (currCourses[i] == 0) {
queue.add(i);
}
}
while (!queue.isEmpty()) {
res++;
int u = queue.poll();
for (int v : prevCourseList.get(u)) {
currCourses[v]--;
if (currCourses[v] == 0) {
queue.add(v);
}
}
}
return res >= numCourses;
}
}
关键点
- 理解入度和出度的概念
- 拓扑排序的概念
- 广度优先遍历寻找学习课程的过程