package com.freetymekiyan.algorithms.level.medium;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
/**
* There are a total of n courses you have to take, labeled from 0 to n - 1.
* <p>
* 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]
* <p>
* Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
* <p>
* For example:
* <p>
* 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.
* <p>
* 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.
* <p>
* Note:
* The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a
* graph is represented.
* <p>
* Hints:
* 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.
* <p>
* Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts of
* Topological Sort.
* <p>
* Topological sort could also be done via BFS.
* <p>
* Tags: Depth-first Search, Breadth-first Search, Graph, Topological Sort
* Similar Problems: (M) Course Schedule II, (M) Graph Valid Tree, (M) Minimum Height Trees
*/
public class CourseSchedule {
/**
* Topological Sort. BFS.
* Detect whether a cycle exists in a directed graph.
* Also make sure it's connected.
* Implementation:
* Build an array of in-degrees of each node.
* Add all 0 in-degree node to the queue.
* Count the number of nodes visited.
* Remove the node from graph by updating in-degrees of all nodes it connects.
* If in-degree becomes 0, add the node to queue.
* Return whether count is numCourses.
*/
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses <= 0) {
return false;
}
// Build in degrees array for each node.
int[] inDegrees = new int[numCourses];
for (int[] p : prerequisites) {
inDegrees[p[0]]++;
}
// Enqueue all 0 in-degree nodes.
Queue<Integer> queue = new ArrayDeque<>();
for (int i = 0; i < inDegrees.length; i++) {
if (inDegrees[i] == 0) {
queue.offer(i);
}
}
// Toposort.
int count = 0;
while (!queue.isEmpty()) {
int c = queue.poll(); // Dequeue node and add to result.
count++;
for (int[] p : prerequisites) {
if (c == p[1]) { // Remove c from graph.
inDegrees[p[0]]--; // Reduce in-degree.
if (inDegrees[p[0]] == 0) {
queue.offer(p[0]);
}
}
}
}
return count == numCourses;
}
/**
* Topological Sort. DFS.
* Detect whether there is a cycle.
* Build an adjacency list with edge list.
* The DFS toposort on the graph.
*/
public boolean canFinishB(int numCourses, int[][] prerequisites) {
List<List<Integer>> graph = new ArrayList<>(numCourses);
for (int i = 0; i < numCourses; i++) {
graph.add(new ArrayList<>());
}
boolean[] visited = new boolean[numCourses];
for (int i = 0; i < prerequisites.length; i++) {
graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
}
for (int i = 0; i < numCourses; i++) {
if (!dfs(graph, visited, i)) {
return false;
}
}
return true;
}
/**
* Toposort. DFS.
* Check temporary mark. If temporary mark is true, there is a cycle. Return false.
* Set temporary mark to true.
* Visit all neighbors first.
* Set temporary mark to false.
* Return true.
*/
private boolean dfs(List<List<Integer>> graph, boolean[] visited, int course) {
if (visited[course]) { // Cycle detected.
return false;
}
visited[course] = true; // Set temp mark.
for (int n : graph.get(course)) {
if (!dfs(graph, visited, n)) {
return false;
}
}
visited[course] = false; // Reset temp mark.
return true;
}
}