/**
* Build the graph using the list of edges.
* The idea is to check is the graph has circle.
* Start from nodes which indegree are zero and traverse the whole graph using BFS.
* A variable count to the indicate the total number of courses we can take.
* If count == numCourses return true.
* Therefore, if the graph has circle, the count must less than total nodes in the graph
*/
public class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// build the graph
HashMap<Integer, HashSet<Integer>> graph = buildGraph(prerequisites);
// calculate indegrees for all nodes in the graph
HashMap<Integer, Integer> map = new HashMap<>();
for (Integer node : graph.keySet()) {
// check the neighbors for a node
for (Integer neighbor : graph.get(node)) {
if (map.containsKey(neighbor)) {
map.put(neighbor, map.get(neighbor)+1);
} else {
map.put(neighbor, 1);
}
}
}
// find all nodes with zero indegree and save them in a queue
// then do the BFS on the graph
Queue<Integer> queue = new LinkedList<>();
HashSet<Integer> set = new HashSet<>();
for (int i=0; i<numCourses; i++) {
if (!map.containsKey(i)) {
queue.offer(i);
set.add(i);
}
}
int count = 0;
while (!queue.isEmpty()) {
count++;
Integer node = queue.poll();
HashSet<Integer> neighbors = graph.get(node);
if (neighbors != null) {
for (Integer neighbor : neighbors) {
// decrease the indegree by 1
map.put(neighbor, map.get(neighbor)-1);
if (map.get(neighbor) == 0) {
queue.offer(neighbor);
set.add(neighbor);
}
}
}
}
// if graph has circle, count must less than # of nodes
return (count == numCourses);
}
private HashMap<Integer, HashSet<Integer>> buildGraph(int[][] prerequisites) {
HashMap<Integer, HashSet<Integer>> graph = new HashMap<>();
for (int i=0; i<prerequisites.length; i++) {
if (graph.containsKey(prerequisites[i][1])) {
graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
} else {
graph.put(prerequisites[i][1], new HashSet<>());
graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
}
}
return graph;
}
}
/**
* Setps:
* 1. Build the graph via the list;
* 2. Using BFS, starts with node that has 0 indegree, traverse through the graph;
* 3. If graph has circle, then total number of nodes counted by BFS must be less than numCourses.
*/
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) {
HashMap<Integer, HashSet<Integer>> graph = graphBuilder(prerequisites);
HashMap<Integer, Integer> degreeMap = getDegree(graph);
HashSet<Integer> zeroIndegreeNodes = getIndegree(degreeMap, numCourses);
// BFS the grpah to get the deepth
Queue<Integer> queue = new LinkedList<Integer>();
for (Integer node : zeroIndegreeNodes) {
queue.offer(node);
}
int nodesCount = 0;
while (!queue.isEmpty()) {
nodesCount++;
int node = queue.poll();
// Get the all neighbor of this node
HashSet<Integer> neighbors = graph.get(node);
if (neighbors != null) {
for (Integer neighbor : neighbors) {
degreeMap.put(neighbor, degreeMap.get(neighbor)-1);
if (degreeMap.get(neighbor) == 0) {
queue.offer(neighbor);
}
}
}
}
return (nodesCount == numCourses);
}
// Key is the node, and value is the neighbors
public HashMap<Integer, HashSet<Integer>> graphBuilder(int[][] prerequisites) {
HashMap<Integer, HashSet<Integer>> map = new HashMap<>();
for (int i=0; i<prerequisites.length; i++) {
// Get the node
int node = prerequisites[i][1];
// Get the neighbor
int neighbor = prerequisites[i][0];
// Create the pair if map doesn't have the record
if (map.containsKey(node)) {
map.get(node).add(neighbor);
} else {
HashSet<Integer> set = new HashSet<>();
set.add(neighbor);
map.put(node, set);
}
}
return map;
}
public HashMap<Integer, Integer> getDegree(HashMap<Integer, HashSet<Integer>> graph) {
HashMap<Integer, Integer> degreeMap = new HashMap<>();
for (Integer node : graph.keySet()) {
for (Integer neighbor : graph.get(node)) {
if (degreeMap.containsKey(neighbor)) {
degreeMap.put(neighbor, degreeMap.get(neighbor)+1);
} else {
degreeMap.put(neighbor, 1);
}
}
}
return degreeMap;
}
// Find all nodes with 0 indegree
public HashSet<Integer> getIndegree(HashMap<Integer, Integer> degreeMap, int numCourses) {
// If course is not in the degreeMap, then the indegree of that course is 0
HashSet<Integer> res = new HashSet<>();
for (int i=0; i<numCourses; i++) {
if (!degreeMap.containsKey(i)) {
res.add(i);
}
}
return res;
}
}