你这个学期必须选修 numCourse
门课程,记为 0
到 numCourse-1
。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
思路与代码
从题意我们可以把这个想象成有向图,如下,课程1需要先修课程2,如果图中存在环,则代表不能完成所有numCourse
门课程。
深度优先搜索
复杂度分析
- 时间复杂度
O(N + M)
: 遍历一个图需要访问所有节点和所有临边,N
和M
分别为节点数量和临边数量; - 空间复杂度
O(N + M)
: 为map所需额外空间,adjacency 长度为N
,并存储M
条临边的数据。
class Solution {
Map<Integer, List<Integer>> map = new HashMap();
int[] state;
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 0 可以完成的课程 ;1 搜索中,还不知道可不可以完成的课程 ;2 还未搜索的课程
state = new int[numCourses];
for (int i = 0; i < prerequisites.length; i++) {
// 用map表示每一门课需要哪些先修课程
if (!map.containsKey(prerequisites[i][0]))
map.put(prerequisites[i][0], new ArrayList());
map.get(prerequisites[i][0]).add(prerequisites[i][1]);
// 需要先修课程暂时标记为还未搜索的课程
state[prerequisites[i][0]] = 2;
}
for (int i = 0; i < numCourses; i++) {
if (state[i] != 0) {
if(!dfs(i)){
return false;
}
}
}
return true;
}
// dfs(i)返回第i门课程是否可以完成
public boolean dfs(int i) {
if (state[i] == 0) return true; // 如果state是0,直接返回true
if (state[i] == 1) return false; // 如果state是1,说明正在搜索中,会形成环
state[i] = 1; // 如果原来未搜索过,则标记为1,表示在搜索中,待学习
for (int preCourse : map.get(i)) {
if (!dfs(preCourse)) return false;
}
state[i] = 0; // 如果该课程的所有先修课程都能够修完,则该课程状态置为0,代表可以完成。
return true;
}
}
广度优先搜索
广度优先搜索和深度优先的方向是反的。adjacency
列表与深度优先中map是反向的。
indegrees[i] = k
表示学完第i门课程还需要k门先修课程,当k变为0
- 时间复杂度
O(N + M)
: 遍历一个图需要访问所有节点和所有临边,N
和M
分别为节点数量和临边数量; - 空间复杂度
O(N + M)
: 为adjacency 所需额外空间,adjacency 长度为N
,并存储M
条临边的数据。
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] indegrees = new int[numCourses];
List<List<Integer>> adjacency = new ArrayList<>();
Queue<Integer> queue = new LinkedList<>();
for(int i = 0; i < numCourses; i++)
adjacency.add(new ArrayList<>());
// Get the indegree and adjacency of every course.
for(int[] cp : prerequisites) {
indegrees[cp[0]]++;
adjacency.get(cp[1]).add(cp[0]);
}
// Get all the courses with the indegree of 0.
for(int i = 0; i < numCourses; i++)
if(indegrees[i] == 0) queue.add(i);
// BFS TopSort.
while(!queue.isEmpty()) {
int pre = queue.poll();
numCourses--;
for(int cur : adjacency.get(pre))
if(--indegrees[cur] == 0) queue.add(cur);
}
return numCourses == 0;
}
}