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?
For example, given 2 and [[1,0]], there are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.
For another example, given 2 and [[1,0],[0,1]], 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.
Analysis
This problem can be converted to finding if a graph contains a cycle.
Java Solution 1 – BFS
This solution uses breath-first search and it is easy to understand.
public boolean canFinish(int numCourses, int[][] prerequisites) { if(prerequisites == null){ throw new IllegalArgumentException("illegal prerequisites array"); } int len = prerequisites.length; if(numCourses == 0 || len == 0){ return true; } // counter for number of prerequisites int[] pCounter = new int[numCourses]; for(int i=0; i<len; i++){ pCounter[prerequisites[i][0]]++; } //store courses that have no prerequisites LinkedList<Integer> queue = new LinkedList<Integer>(); for(int i=0; i<numCourses; i++){ if(pCounter[i]==0){ queue.add(i); } } // number of courses that have no prerequisites int numNoPre = queue.size(); while(!queue.isEmpty()){ int top = queue.remove(); for(int i=0; i<len; i++){ // if a course's prerequisite can be satisfied by a course in queue if(prerequisites[i][1]==top){ pCounter[prerequisites[i][0]]--; if(pCounter[prerequisites[i][0]]==0){ numNoPre++; queue.add(prerequisites[i][0]); } } } } return numNoPre == numCourses; } |
Java Solution 2 – DFS
public boolean canFinish(int numCourses, int[][] prerequisites) { if(prerequisites == null){ throw new IllegalArgumentException("illegal prerequisites array"); } int len = prerequisites.length; if(numCourses == 0 || len == 0){ return true; } //track visited courses int[] visit = new int[numCourses]; // use the map to store what courses depend on a course HashMap<Integer,ArrayList<Integer>> map = new HashMap<Integer,ArrayList<Integer>>(); for(int[] a: prerequisites){ if(map.containsKey(a[1])){ map.get(a[1]).add(a[0]); }else{ ArrayList<Integer> l = new ArrayList<Integer>(); l.add(a[0]); map.put(a[1], l); } } for(int i=0; i<numCourses; i++){ if(!canFinishDFS(map, visit, i)) return false; } return true; } private boolean canFinishDFS(HashMap<Integer,ArrayList<Integer>> map, int[] visit, int i){ if(visit[i]==-1) return false; if(visit[i]==1) return true; visit[i]=-1; if(map.containsKey(i)){ for(int j: map.get(i)){ if(!canFinishDFS(map, visit, j)) return false; } } visit[i]=1; return true; } |
public class Courses {
private static boolean canFinish(int num, int[][] prerequisites){
Set set = new HashSet();
for(int i=0; i<num; i++){
if(dfs(i, prerequisites, set)) {
return true;
}
}
return false;
}
private static boolean dfs(int start, int[][] prerequisites, Set set) {
set.add(start);
int[] dependencies = getDependencies(start, prerequisites);
for(int i: dependencies) {
if(set.contains(i)) {
return true;
} else if(dfs(i, prerequisites, set)) {
return true;
}
}
set.remove(start);
return false;
}
private static int[] getDependencies(int start, int[][] prerequisites) {
List al = new ArrayList();
for(int i=0; i<prerequisites.length; i++) {
if(prerequisites[i][0] == start) {
al.add(prerequisites[i][1]);
}
}
int[] ret = new int[al.size()];
for(int i=0; i<al.size(); i++) {
ret[i] = al.get(i);
}
return ret;
}
public static void main(String[] args) {
int[][] testcase = {{0,1},{1,2},{2,0},{4,0},{3,4}};
int[][] testcase2 = {{0,1},{1,2},{2,4},{0,4},{4,3}};
int[][] testcase3 = {{0,1},{1,0}};
int[][] testcase4 = {{0,1}};
int[][] testcase5 = {{0,1},{1,2},{2,4},{0,4},{4,3},{3,0}};
System.out.println(canFinish(testcase5.length, testcase5));
}
}
What are the time complexities of both methods?
The video link seems to be broken.
i posted a perfect solution which got deleted, thanks
Isn’t going to much easier to read and code if a Graph holder class is defined and its object is populated with the input i.e. prerequisites. Graph class will hold the adjacency lists for every course.
And basically this problem will boil down to “Detect cycle in a DIRECTED graph”.
The problem declaration is ambiguous. If “one node is reachable only if ALL of its parents are reached”,then your detecting cycle solution is right. Else if “one node is reachable if ONE of its parent is reached”, then soluition is much simpler, because you only need to find “independent nodes”, and to see if they are connected to other nodes.
The key is how to detect cycle!