207. Course Schedule & 210. Course Schedule II
Question
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] = [ai, bi]
indicates that you must take course bi
first if you want to take course ai
.
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
.
Algorithm
This is a typical question utilizing topological sorting. A topological sort or topological ordering of a directed graph is a linear ordering of its vertices such that for every directed edge uv from vertex u to vertex v, u comes before v in the ordering.
A topological sort is a graph traversal in which each node v is visited only after all its dependencies are visited*.* A topological ordering is possible if and only if the graph has no directed cycles, that is, if it is a directed acyclic graph (DAG).
Any DAG has at least one topological ordering, and algorithms are known for constructing a topological ordering of any DAG in linear time.
The detailed algorithm pseudo code could be found on Wiki. I'd list the implementation of DFS and BFS with comments.
Code
207
This question doesn't require you to have the result list, you only need to return if it can finish(no cycle). If you do question 210 first, this question would be easy.We just check if the result traverse all the elements, which compare length of result set and number of course.
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] indegrees = new int[numCourses];
for (int[] prerequisite : prerequisites) {
indegrees[prerequisite[0]]++;
}
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
if (indegrees[i] == 0) {
queue.offer(i);
}
}
while (!queue.isEmpty()) {
int course = queue.poll();
for (int[] prerequisite : prerequisites) {
if (course == prerequisite[1]) {
indegrees[prerequisite[0]]--;
if (indegrees[prerequisite[0]] == 0) {
queue.offer(prerequisite[0]);
}
}
}
}
for (int indegree : indegrees) {
if (indegree != 0) {
return false;
}
}
return true;
}
}
210
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
int[] indegrees = new int[numCourses];
int[] res = new int[numCourses];
int k = 0;
for (int[] prerequisite : prerequisites) {
indegrees[prerequisite[0]]++;
}
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
if (indegrees[i] == 0) {
queue.offer(i);
res[k++] = i;
}
}
while (!queue.isEmpty()) {
int course = queue.poll();
for (int[] prerequisite : prerequisites) {
if (course == prerequisite[1]) {
indegrees[prerequisite[0]]--;
if (indegrees[prerequisite[0]] == 0) {
queue.offer(prerequisite[0]);
res[k++] = prerequisite[0];
}
}
}
}
return k == numCourses ? res : new int[0];
}
}
DFS Implementation
package Graph.topologicalSort;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by szhang on 2023/4/19
*/
public class DFSImplementation {
public static void main(String[] args) {
int[][] prerequisites = new int[1][1];
prerequisites[0] = new int[]{0, 1};
int numOfCourses = 2;
int[] res = findRes(prerequisites, numOfCourses);
for (int i: res
) {
System.out.println(i);
}
}
public static int[] findRes(int[][] prerequisites, int numOfCourses) {
List<Integer> res = new ArrayList<>();
List<List<Integer>> graph = new ArrayList<>();
// build graph
for (int i = 0; i < numOfCourses; i++) {
graph.add(new ArrayList<>());
}
for (int[] prerequisite: prerequisites) {
graph.get(prerequisite[0]).add(prerequisite[1]);
}
// build visit status, 0:unvisited, 1:visiting, 2:visited
Map<Integer, Integer> visit = new HashMap<>();
// mark all node unvisited;
for (int i = 0; i < numOfCourses; i++) {
visit.put(i, 0);
}
//
for (int i = 0; i < numOfCourses; i++) {
if (visit.get(i) != 2) { // if the node is unvisited, check if there is cycle start dfs from it
if (hasCycle(res, graph, i, visit)) {
return new int[]{}; // has cycle, return empty array;
}
}
}
// the node in dfs is added in reverse order, so the output should be reversely out put to get the right order
int[] ret = new int[numOfCourses];
int index = 0;
for (int i = 0; i < res.size(); i++) {
ret[index++] = res.get(res.size() - 1 - i);
}
return ret;
}
private static boolean hasCycle(List<Integer> res, List<List<Integer>> graph, int i, Map<Integer, Integer> visit) {
if (visit.get(i) == 2) { // node has been visited
return false;
} else if (visit.get(i) == 1) { // node is being visiting
return true;
}
visit.put(i, 1);
for (int j = 0; j < graph.get(i).size(); j++) {
hasCycle(res, graph, graph.get(i).get(j), visit);
}
visit.put(i, 2);
res.add(i);
return false;
}
}