5a3c1b2d创建于 2025年8月3日历史提交
/**
 * https://leetcode.com/problems/course-schedule/
 * Time O((V)^2 + E) | Space O(V + E)
 * @param {number} numCourses
 * @param {number[][]} prerequisites
 * @return {boolean}
 */
var canFinish = function (numCourses, prerequisites) {
    const { graph, path } = buildGraph(numCourses, prerequisites);

    return hasPath(numCourses, graph, path);
};

var initGraph = (numCourses) => ({
    graph: new Array(numCourses).fill().map(() => []),
    path: new Array(numCourses).fill(false),
});

var buildGraph = (numCourses, prerequisites) => {
    const { graph, path } = initGraph(numCourses);

    for (const [src, dst] of prerequisites) {
        const neighbors = graph[dst] || [];

        neighbors.push(src);

        graph[dst] = neighbors;
    }

    return { graph, path };
};

var hasPath = (numCourses, graph, path) => {
    for (let course = 0; course < numCourses; course++) {
        if (isCyclic(course, graph, path)) return false;
    }

    return true;
};

var isCyclic = (currCourse, graph, path) => {
    const hasSeen = path[currCourse];
    if (hasSeen) return true;

    const isMissingNext = !(currCourse in graph);
    if (isMissingNext) return false;

    return backTrack(currCourse, graph, path);
};

var backTrack = (currCourse, graph, path) => {
    path[currCourse] = true;
    const _hasCycle = hasCycle(currCourse, graph, path);
    path[currCourse] = false;

    return _hasCycle;
};

var hasCycle = (currCourse, graph, path) => {
    for (const neighbor of graph[currCourse]) {
        if (isCyclic(neighbor, graph, path)) return true;
    }

    return false;
};

/**
 * https://leetcode.com/problems/course-schedule/
 * Time O(V + E) | Space O(V + E)
 * @param {number} numCourses
 * @param {number[][]} prerequisites
 * @return {boolean}
 */
var canFinish = function (numCourses, prerequisites) {
    const { graph, visited, path } = buildGraph(numCourses, prerequisites);

    for (let currCourse = 0; currCourse < numCourses; currCourse++) {
        if (isCyclic(currCourse, graph, visited, path)) return false;
    }

    return true;
};

var initGraph = (numCourses) => ({
    graph: new Array(numCourses).fill().map(() => []),
    visited: new Array(numCourses).fill(false),
    path: new Array(numCourses).fill(false),
});

var buildGraph = (numCourses, prerequisites) => {
    const { graph, visited, path } = initGraph(numCourses);

    for (const [src, dst] of prerequisites) {
        const neighbors = graph[dst] || [];

        neighbors.push(src);

        graph[dst] = neighbors;
    }

    return { graph, visited, path };
};

var isCyclic = (currCourse, graph, visited, path) => {
    const isVisited = visited[currCourse];
    if (isVisited) return false;

    const hasSeen = path[currCourse];
    if (hasSeen) return true;

    const isMissingNext = !(currCourse in graph);
    if (isMissingNext) return false;

    const _isCyclic = backTrack(currCourse, graph, visited, path);

    visited[currCourse] = true;

    return _isCyclic;
};

var backTrack = (currCourse, graph, visited, path) => {
    path[currCourse] = true;
    const _hasCycle = hasCycle(currCourse, graph, visited, path);
    path[currCourse] = false;

    return _hasCycle;
};

var hasCycle = (currCourse, graph, visited, path) => {
    for (const neighbor of graph[currCourse]) {
        if (isCyclic(neighbor, graph, visited, path)) return true;
    }

    return false;
};

/**
 * https://leetcode.com/problems/course-schedule/
 * Time O(V + E) | Space O(V + E)
 * @param {number} numCourses
 * @param {number[][]} prerequisites
 * @return {boolean}
 */
var canFinish = function (numCourses, prerequisites) {
    const { graph, indegree } = buildGraph(numCourses, prerequisites);
    const topologicalOrder = topologicalSort(graph, indegree);
    const isDirectedAcyclicGraph = topologicalOrder.length === numCourses;

    return isDirectedAcyclicGraph;
};

var initGraph = (numCourses) => ({
    graph: new Array(numCourses).fill().map(() => []),
    indegree: new Array(numCourses).fill(0),
});

var buildGraph = (numCourses, prerequisites) => {
    const { graph, indegree } = initGraph(numCourses);

    for (const [src, dst] of prerequisites) {
        graph[src].push(dst);
        indegree[dst]++;
    }

    return { graph, indegree };
};

var topologicalSort = (graph, indegree, order = []) => {
    const queue = searchGraph(graph, indegree);

    bfs(graph, indegree, queue, order);

    return order;
};

var searchGraph = (graph, indegree, queue = new Queue([])) => {
    for (const node in graph) {
        const isSource = indegree[node] === 0;
        if (isSource) queue.enqueue(node);
    }

    return queue;
};

var bfs = (graph, indegree, queue, order) => {
    while (!queue.isEmpty()) {
        for (let i = queue.size() - 1; 0 <= i; i--) {
            checkNeighbors(graph, indegree, queue, order);
        }
    }
};

var checkNeighbors = (graph, indegree, queue, order) => {
    const node = queue.dequeue();

    order.push(node);

    for (const neighbor of graph[node]) {
        indegree[neighbor]--;

        const isSource = indegree[neighbor] === 0;
        if (isSource) queue.enqueue(neighbor);
    }
};