1.题目描述
你这个学期必须选修
numCourses
门课程,记为0
到numCourses - 1
。在选修某些课程之前需要一些先修课程。 先修课程按数组
prerequisites
给出,其中prerequisites[i] = [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
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i]
中的所有课程对 互不相同
2.解题思路
使用两个hashMap,其中inDegree存储每个节点的前驱结点,即记录每门课程学习之前需要先修哪些课程,outDegree存储每个节点的后继结点,即记录每门课程学习后可以学习哪些课程;遍历prerequisites后给inDegree和outDegree分别赋值,接着借助一个队列实现对图的广度优先遍历,即先把所有入度为0的结点加入到队列中,在访问这些结点的过程中,假设这个结点为cur,那么学完它就可以继续去学习outDegree.get(cur)中的课程,于是就可以把这些课程的先修课中为cur的值删除掉,因为我们已经学过了cur,在删除这门课后,如果这门课程的先修课程数已经为0,就可以将它加入到queue中,继续删除以它为先修课程的结点,并在每次访问queue中结点时都将learned[cur]置为true,当队列中结点为空时,我们就去查看learned中是否还有未学习过的课程,如果有就返回false,否则返回true
3.代码实现
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
//借助一个队列实现广度优先遍历
boolean[] learned = new boolean[numCourses];
Map<Integer,List<Integer>> inDegree = new HashMap<>();
Map<Integer,List<Integer>> outDegree = new HashMap<>();
//初始化map 其中inDegree保存每门课程的先修课程
//outDegree 保存每门课程的后序课程
for (int i = 0; i < numCourses; i++) {
inDegree.put(i,new ArrayList<>());
outDegree.put(i,new ArrayList<>());
}
//遍历prerequisites给map赋值
for (int i = 0; i < prerequisites.length; i++) {
int cur = prerequisites[i][0];
int pre = prerequisites[i][1];
List<Integer> outs = outDegree.get(pre);
List<Integer> ins = inDegree.get(cur);
ins.add(pre);
outs.add(cur);
outDegree.put(pre,outs);
inDegree.put(cur,ins);
}
//把入度为0的结点入队
Queue<Integer> queue = new LinkedList<>();
for (Integer i : inDegree.keySet()) {
if (inDegree.get(i).size() == 0) {
queue.offer(i);
}
}
//依次将队列中的结点出队,并在后面遇到入度为0的结点时继续入队
while (!queue.isEmpty()) {
Integer cur = queue.poll();
learned[cur] = true;
for (int x : outDegree.get(cur)) {
List<Integer> pres = inDegree.get(x);
pres.remove(cur);
if (pres.size() == 0) {
queue.offer(x);
} else {
inDegree.put(x,pres);
}
}
}
//退出循环后,检查是否仍有未学习过的课程
for (int i = 0; i < numCourses; i++) {
if (!learned[i]) {
return false;
}
}
return true;
}
}