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:
2, [[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.
2, [[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.
Note:
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
Hints:
The problem is equivalent to finding if a directed graph contains a circle. Thus we could use a DFS or BFS solution.
DFS Solution:
Use an array to mark if the node has been visited before. Here is a small trick to save some time. Instead of using a boolean array, we use an integer array int[] visited.
visited = -1, means this node has been visited.
visited = 1, means this node has been validated which does not include a circle. Thus if we saw that a node has been validated, we don't need to calculate again to find out the circle starting from this node. e.g. [0, 1] [1, 2] [2, 3] [3, 4]. For the node 0, we have already validated 2 3 and 4 do not have a circle. Thus we don't need to calculate for the node 2 3 4 again.
Code (Java):
Update on 4/22/19: Using topological sorting
- This problem is equivalent to finding if a cycle exists in a directed graph. If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses.
- Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts of Topological Sort.
- Topological sort could also be done via BFS.
The problem is equivalent to finding if a directed graph contains a circle. Thus we could use a DFS or BFS solution.
DFS Solution:
Use an array to mark if the node has been visited before. Here is a small trick to save some time. Instead of using a boolean array, we use an integer array int[] visited.
visited = -1, means this node has been visited.
visited = 1, means this node has been validated which does not include a circle. Thus if we saw that a node has been validated, we don't need to calculate again to find out the circle starting from this node. e.g. [0, 1] [1, 2] [2, 3] [3, 4]. For the node 0, we have already validated 2 3 and 4 do not have a circle. Thus we don't need to calculate for the node 2 3 4 again.
Code (Java):
public class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses <= 0) {
return true;
}
if (prerequisites == null || prerequisites.length == 0) {
return true;
}
// First transform the edge list to adj. list
Map<Integer, List<Integer>> adjList = new HashMap<>();
for (int[] edge : prerequisites) {
if (adjList.containsKey(edge[0])) {
List<Integer> neighbors = adjList.get(edge[0]);
neighbors.add(edge[1]);
adjList.put(edge[0], neighbors);
} else {
List<Integer> neighbors = new ArrayList<Integer>();
neighbors.add(edge[1]);
adjList.put(edge[0], neighbors);
}
}
int[] visited = new int[numCourses];
// Check if the graph contains a circle, if yes, return false.
for (int i = 0; i < numCourses; i++) {
if (hasCircles(i, visited, adjList)) {
return false;
}
}
return true;
}
private boolean hasCircles(int vertexId, int[] visited, Map<Integer, List<Integer>> adjList) {
if (visited[vertexId] == -1) {
return true;
}
if (visited[vertexId] == 1) {
return false;
}
visited[vertexId] = -1;
List<Integer> neighbors = adjList.get(vertexId);
if (neighbors != null) {
for (int neighbor : neighbors) {
if (hasCircles(neighbor, visited, adjList)) {
return true;
}
}
}
visited[vertexId] = 1;
return false;
}
}
Update on 4/22/19: Using topological sorting
public class Solution {
/*
* @param numCourses: a total of n courses
* @param prerequisites: a list of prerequisite pairs
* @return: true if can finish all courses or false
*/
public boolean canFinish(int numCourses, int[][] prerequisites) {
// write your code here
if (numCourses <= 0 || prerequisites == null || prerequisites.length == 0) {
return true;
}
// step 1: create adjlist and in-=degree map
//
Map<Integer, Integer> nodeToIndegreeMap = new HashMap<>();
Map<Integer, List<Integer>> adjList = new HashMap<>();
for (int i = 0; i < numCourses; i++) {
nodeToIndegreeMap.put(i, 0);
adjList.put(i, new ArrayList<>());
}
for (int[] prerequisite : prerequisites) {
int inDegree = nodeToIndegreeMap.get(prerequisite[1]);
inDegree += 1;
nodeToIndegreeMap.put(prerequisite[1], inDegree);
}
for (int[] prerequisite : prerequisites) {
List<Integer> neighbors = adjList.get(prerequisite[0]);
neighbors.add(prerequisite[1]);
adjList.put(prerequisite[0], neighbors);
}
// step 2: get all nodes with indegree 0 and put into the queuue
//
Queue<Integer> queue = new LinkedList<>();
int numNodes = 0;
for (Integer key : nodeToIndegreeMap.keySet()) {
int indegree = nodeToIndegreeMap.get(key);
if (indegree == 0) {
queue.offer(key);
numNodes += 1;
}
}
while (!queue.isEmpty()) {
int curNode = queue.poll();
for (int neighbor : adjList.get(curNode)) {
int indegree = nodeToIndegreeMap.get(neighbor);
indegree -= 1;
nodeToIndegreeMap.put(neighbor, indegree);
if (indegree == 0) {
queue.offer(neighbor);
numNodes += 1;
}
}
}
return numNodes == numCourses;
}
}
No comments:
Post a Comment