There are a total of n courses you have to take, labeled from 0 to n-1.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
Example 1:
Input: 2, [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.
Example 2:
Input: 2, [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should
also have finished course 1. So it is impossible.
Note:
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
You may assume that there are no duplicate edges in the input prerequisites.
所有需要学习的课程和其前置课程可以合成一张图;然后可以用经典BFS和DFS来完成题目。
BFS的主要思想是:每有一个可以学习的课程(前置课程为0或者前置课程已经学习完),学习它(已学习课程数加1),然后将所有以它为前置课程的课程所需前置课程数减1。依次,当所有可以学习的课程学习完后,查看是否满足总课程数。代码如下:
public boolean canFinish(int numCourses, int[][] prerequisites) {
boolean[][] matrix = new boolean[numCourses][numCourses];//matrix[i][j]=true表示学习课程j需要先学习课程i
int[] value = new int[numCourses];//value[i]表示学习课程i还需要的前置课程数量
for(int i = 0; i < prerequisites.length; ++i) {
int ready = prerequisites[i][0];//要学习的课程
int pre = prerequisites[i][1];//所需的前置课程
if(!matrix[pre][ready]) {
value[ready]++;
}
matrix[pre][ready] = true;
}
Queue<Integer> queue = new ArrayDeque<>();//已经可以学习的课程队列
for(int i = 0; i < numCourses; ++i) {//将所需前置课程数为0(即可以学习的课程)加入队列
if(value[i] == 0) {
queue.add(i);
}
}
int count = 0;//表示已经学习的课程数
while(!queue.isEmpty()) {//每次出队列的课程表示已经学习,将以该课程为前置课程的value减1
int course = queue.poll();
count++;
for(int i = 0; i < numCourses; ++i) {
if(matrix[course][i]) {//课程i需要前置课程course
if(--value[i] == 0) {//前置课程course已经学习,课程i所需前置课程减1;若课程i所需前置课程为0,则表示可以学习,加入队列
queue.add(i);
}
}
}
}
return count == numCourses;
}
DFS的主要思想是:将所有课程构成一个有向图;边的起点表示前置课程,边的终点表示要学习的课程。对该有向图,要学习某一个课程,以该节点为起点进行深度优先搜索,若回到该节点(即构成一个环),则该课程无法完成学习,题目返回false。代码如下:
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> graph = new ArrayList<>(numCourses);
for(int i = 0; i < numCourses; ++i) {
graph.add(new ArrayList<>());
}
for(int i = 0; i < prerequisites.length; ++i) {//将所有课程构成一个有向图;边的起点表示前置课程,边的终点表示要学习的课程
graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
}
boolean[] visited = new boolean[numCourses];//记录已经经过的节点
for(int i = 0; i < numCourses; ++i) {//依次以每个课程节点为起点开始深度优先搜索,如果重复访问,则表示存在环,则不满足题意
if(!dfs(graph, visited, i)) {
return false;
}
}
return true;
}
private boolean dfs(List<List<Integer>> graph, boolean[] visited, int i) {
if(visited[i]) {
return false;
} else {
visited[i] = true;
}
for(int j = 0; j < graph.get(i).size(); ++j) {
if(!dfs(graph, visited, graph.get(i).get(j))) {
return false;
}
}
visited[i] = false;
return true;
}
上面为DFS方法,下面为BFS方法。明显看到DFS方法耗时多很多;这是因为DFS搜索的次数比BFS搜索的次数要多好多。