Source: https://leetcode.com/problems/course-schedule/

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.

/**
* @param {number} numCourses
* @param {number[][]} prerequisites
* @return {boolean}
*/
// BFS Topological Sort
// O(E + V) time, space
var canFinish = function(numCourses, prerequisites) {
// Initialize indegrees array to keep track of number of prereqs required for a course
const indegrees = Array.from({ length: numCourses }, () => 0);
// Initialize adjacency list (prereqs -> list of dependent courses)
const courseList = Array.from({ length: numCourses }, () => []);
// Loop through all the prereqs to build adjacency list (prereqs -> list of dependent courses) and fill in indegrees of dependent courses
prerequisites.forEach((prerequisite) => {
const [dependentCourse, prereqCourse] = prerequisite;
courseList[prereqCourse].push(dependentCourse);
indegrees[dependentCourse]++;
});
// Initialize queue of courses we can take
const queue = [];
// Loop through all indegrees and if there is a node with 0 indegree (a course that doesn't have any prereq),
// We add it to the queue
indegrees.forEach((indegree, course) => {
if (indegree === 0) {
queue.push(course);
}
});
// If there are no nodes with indegree 0, it's not a DAG/we cannot finish all courses because we can't take any prereqs to start
const noCoursesToTake = !indegrees.some((indegree) => indegree === 0)
if (noCoursesToTake) {
return false;
}
// Initialize the number of valid courses we are able to take
let numValidCourses = 0;
// While the queue is not empty
while (queue.length > 0) {
// Dequeue the valid course
const validCourse = queue.shift();
// Increment the number of courses we are able to take
numValidCourses++;
// Loop through all the dependent courses that this valid course is a prereq of
courseList[validCourse].forEach((dependentCourse) => {
// Decrement the indegree of the dependent course
indegrees[dependentCourse]--;
// If the dependent course's indegree is 0
if (indegrees[dependentCourse] === 0) {
// Enqueue the dependent course as we can take it
queue.push(dependentCourse);
}
});
}
// If the number of courses we are able to take equals the total number of courses, we can finish all courses
return numValidCourses === numCourses;
};