Observation: this problem is equivalent to finding a reverse topological sort of the course prerequisite graph (if it exists). A valid course order will exist if the course prerequisite graph is acyclic.
Our algorithm consists of two parts:
In Approach 1 for Course Schedule, we saw how to modify DFS to determine if a graph is acyclic. Using this as a starting point, we only need to make a minor change to our code to store the reverse topological sort in our answer array: simply append vertices to the answer array as they finish during DFS. I have highlighted these changes below:
// returns true if the graph is acyclic
boolean DFS() {
for (int u = 0; u < numVertices; u++)
if (color[u] == WHITE && visit(u))
return false;
return true;
}
// returns true if a cycle is found
boolean visit(int u) {
color[u] = GRAY;
for (int v : adjlist[u])
if (color[v] == WHITE && visit(v) || color[v] == GRAY)
return true;
color[u] = BLACK;
return false;
}
// returns a reverse topological sort
int[] DFS() {
for (int u = 0; u < numVertices; u++)
if (color[u] == WHITE && visit(u))
return new int[0];
return answer;
}
// returns true if a cycle is found
boolean visit(int u) {
color[u] = GRAY;
for (int v : adjlist[u])
if (color[v] == WHITE && visit(v) || color[v] == GRAY)
return true;
color[u] = BLACK;
// as vertices finish, store them in the answer array
answer[index++] = u;
return false;
}
Here is the full commented solution:
The running time of DFS is \(O(|V|+|E|)\). In this case, the number of vertices (numCourses) is at most \(2N\) (the worst case happens when every prerequisite pair contains two unique courses) and the number of edges (number of prerequisites) is \(N\). Therefore, the total running time is \(O(N)\).
The space required by an adjacency list is \(O(|V|+|E|)\). As stated above, the number of vertices is at most \(2N\) and the number of edges is \(N\). In addition, the color and answer arrays have length \(N\) and recursive calls take \(O(N)\) stack frames. Therefore, the total space required is \(O(N)\).