試題
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.
代碼
有以下思路:
1、對於沒有入度出度的節點可以直接完成,對於沒有入度的節點也可以直接完成。
2、有入度的節點必須要依賴的節點都完成了,才能夠完成。我們可以統計每個節點的依賴完成數量。
3、使用寬度優先,先完成1中節點,然後當2中節點的依賴完成後才能完成。
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
if(prerequisites.length == 0) return true;
HashSet<Integer> start = new HashSet<>();
HashMap<Integer, ArrayList<Integer>> graph = new HashMap<>();
Stack<Integer> stack = new Stack<>();
int[] sum = new int[numCourses];
for(int i = 0; i < numCourses; i++){
start.add(i);
}
for(int[] e : prerequisites){
sum[e[1]]++;
if(start.contains(e[1])){
start.remove(e[1]);
}
if(graph.containsKey(e[0])){
graph.get(e[0]).add(e[1]);
}else{
ArrayList<Integer> temp = new ArrayList<>();
temp.add(e[1]);
graph.put(e[0], temp);
}
}
if(start.size() == 0){
return false;
}
for(int s : start){
stack.push(s);
}
int count = 0;
while(!stack.isEmpty()){
int size = stack.size();
for(int i = 0; i < size; i++){
int cur = stack.pop();
count++;
if(graph.containsKey(cur)){
ArrayList<Integer> nxt = graph.get(cur);
for(int n : nxt){
sum[n]--;
if(sum[n] == 0){
stack.push(n);
}
}
}
}
}
if(count == numCourses){
return true;
}
return false;
}
}
另外其實這題有個特點是如果無法完成,那麼說明圖中存在一個環。所以可快速判斷圖中是否有環。
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses == 0 || prerequisites == null || prerequisites.length == 0) return true; // ??
// Index of this list is a course and its values are the dependencies (children)
List<List<Integer>> courseDependency = new ArrayList<>(numCourses);
for (int i = 0; i < numCourses; i++)
courseDependency.add(new ArrayList<>());
/*
create following courseDependency arraylist for each index (course)
0: [1,2],
1: [2],
2: []
*/
for (int i = 0; i < prerequisites.length; i++) {
courseDependency.get(prerequisites[i][0]).add(prerequisites[i][1]);
}
// use state int array to represent current status of visiting for each node
// 0: not visited yet, 1: currently visiting (in recursion stack frame), 2: already visited
int[] state = new int[numCourses];
for (int i = 0; i < numCourses; i++) {
if (!dfs(i, courseDependency, state)) {
return false; // Cycle found
}
}
// no cycle found
return true;
}
private boolean dfs(int course, List<List<Integer>> courseDependency, int[] state) {
state[course] = 1; // currently visiting
List<Integer> dependencies = courseDependency.get(course);
for (int children: dependencies) {
if (state[children] == 1) // If any children is also being currently visiting, then cycle is detected
return false;
if (state[children] == 0) { // Not visted yet
if (!dfs(children, courseDependency, state))
return false;
}
}
state[course] = 2; // done visiting
return true; // no cycle, backtrack to caller
}
}