Skip to main content

Course Schedule


Course Schedule: There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [a_i, b_i] indicates that you must take course b_i first if you want to take course a_i.

For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1. Return true if you can finish all courses. Otherwise, return false.

Example 1:
Input: numCourses = 2, prerequisites = [[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: numCourses = 2, prerequisites = [[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.

Constraints:
  • 1 <= numCourses <= 2000
  • 0 <= prerequisites.length <= 5000
  • prerequisites[i].length == 2
  • 0 <= a_i, b_i < numCourses
  • All the pairs prerequisites[i] are unique.

Try this Problem on your own or check similar problems:

  1. Course Schedule II
  2. Minimum Height Trees
  3. Course Schedule III
Solution:
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<Integer>[] graph = new List[numCourses];
int[] degree = new int[numCourses];

for(int i = 0; i < graph.length; ++i){
graph[i] = new ArrayList<>();
}

for(int[] p: prerequisites){
graph[p[1]].add(p[0]);
++degree[p[0]];
}

Queue<Integer> q = new LinkedList<>();

for(int i = 0; i < degree.length; ++i){
if(degree[i] == 0) q.add(i);
}

int count = 0;

while(!q.isEmpty()){
int node = q.poll();
++count;
for(int neighbour: graph[node]){
if(--degree[neighbour] == 0) q.add(neighbour);
}
}

return count == numCourses;
}

Time/Space Complexity:
  • Time Complexity: O(E+V) where E number of edges and V number of vertices
  • Space Complexity: O(E+V)

Explanation:

Whenever you have a clear relation between different elements given by the problem statement you should think if you can reframe the initial problem to a graph problem. In our case the elements are courses, and the obvious relations are given by prerequisites. We can only finish all courses if there are no cycles in the graph (second example, you can't start course 0 before you start 1, but you can't start course 1 before you start 0 so it's impossible to finish the course since if modeled courses as a graph we would have a cycle). To find if graph contains cycle we perform topological sort: Topological sort of directed graph is a linear ordering of its vertices such that, for every directed edge U -> V from vertex U to vertex V, U comes before V in the ordering. Why does topological sort detect cycle in graph? In Topological Sort, the idea is to visit the parent node followed by the child node. If the given graph contains a cycle, then there is at least one node which is a parent as well as a child thus breaking topological order. So, if we can visit all elements count == numCourses we know there is an existing topological order, otherwise if there is a cycle topological order would break and we wouldn't have visited all elements.

So how do we perform topological sort:

  • We represent the graph in adjacency-list representation (each node mapped to its neighbor), also we track the degree of a node (number of incoming edges to each node)
  • We use prerequisites array to form the adjacency-lists and increase the degree for each node
  • Next, a queue is created with initial candidates picked so they have degree == 0 (course that can start without waiting for completion of any other course)
  • Each time we poll another course from the queue of candidates we check its neighbors and if there are any potential candidates after we have visited the current node which we can be added to the queue (--degree[neighbour] == 0)

For space complexity we have O(E+V) since we're building a graph of E edges and V vertices. Since we visit all edges and vertices in the topological sort we have O(V+E) time complexity.