Published 2024-07-10

0207. Course Schedule

leetcode

Submission

Problem:

The problem is the Course Schedule problem (often referred to as Leetcode 207). The given problem can be restated as follows:

You are given a total of `numCourses`, labeled from `0` to `numCourses-1`. You are also given an array `prerequisites` where `prerequisites[i] = [ai, bi]` indicates that in order to take course `ai`, you must first take course `bi`.

Write a function that determines if it is possible to finish all courses. If there is a cyclic dependency, it is impossible to finish all courses.

- For example:
    - Input: numCourses = 2, prerequisites = [[1, 0]]
    - Output: True
    - Explanation: There are a total of 2 courses to take. To take course 1, you must first take course 0. So it is possible.

Solution Description:

To implement this, we need to represent the courses and their prerequisites as a directed graph and then check if the graph has any cycles. If it has a cycle, it means there is a circular dependency, and the courses cannot all be finished. We can use Depth-First Search (DFS) to detect cycles in the graph.

Here’s the plan:

  1. Construct a graph using adjacency lists.
  2. Maintain a state for each course (unvisited, visiting, visited).
  3. Perform DFS on each unvisited course to detect cycles.

Time Complexity: O(V + E) where V is the number of courses and E is the number of prerequisites since we might visit each course and prerequisite once. Space Complexity: O(V + E) to store the graph and the state of each course.

Example:

Let’s take an example to clarify:
    - Input: numCourses = 4, prerequisites = [[1, 0], [2, 1], [3, 2]]
    - The graph would look like:
        0 → 1 → 2 → 3 
    - We perform a DFS starting from course 0, then 1, then 2, then 3. There’s no cycle, so the output is true.

Setup:

Below is the general framework for implementing the solution using modern JavaScript. The `console.log` statements will help identify the input, actual result, and expected result.

/**
 * Determines if you can finish all courses given the prerequisites.
 * @param {number} numCourses - Total number of courses
 * @param {number[][]} prerequisites - List of prerequisite pairs
 * @returns {boolean} True if all courses can be finished, otherwise False
 */
function canFinish(numCourses, prerequisites) {
    // Helper logging functions for environments without console support
    const log = typeof NestedInteger !== 'undefined' ? () => {} : console.log;
    const table = typeof NestedInteger !== 'undefined' ? () => {} : console.table;

    if (prerequisites.length === 0) {
        return true;
    }

    let g = Array(numCourses).fill(0).map(n => []);

    const UNVISITED = 0;
    const VISITING = 1;
    const VISITED = 2;
    const state = Array(numCourses).fill(UNVISITED);

    for (const [course, prereq] of prerequisites) {
        g[prereq].push(course);
    }
    table(g);

    function hasCycle(v) {
        if (state[v] === VISITING) {
            return true;
        }
        if (state[v] === VISITED) {
            return false;
        }

        state[v] = VISITING;
        for (const neighbor of g[v]) {
            if (hasCycle(neighbor)) {
                return true;
            }
        }
        state[v] = VISITED;
        return false;
    }

    for (let i = 0; i < numCourses; i++) {
        if (hasCycle(i)) {
            log(`Cycle found! i = ${i}`);
            return false;
        }
    }
    return true;
}

// Test cases
const testCases = [
    { numCourses: 5, prerequisites: [[1,4],[2,4],[3,1],[3,2]], expected: true },
    { numCourses: 2, prerequisites: [[1, 0]], expected: true },
    { numCourses: 2, prerequisites: [[1, 0], [0, 1]], expected: false },
    { numCourses: 4, prerequisites: [[1, 0], [2, 1], [3, 2]], expected: true },
    { numCourses: 5, prerequisites: [[1, 0], [2, 1], [3, 2], [4, 3], [3, 1]], expected: true },
    { numCourses: 3, prerequisites: [[0, 1], [1, 2], [2, 0]], expected: false },
    // Additional test cases covering various scenarios
    { numCourses: 1, prerequisites: [], expected: true },
    { numCourses: 20, prerequisites: [[0,10],[3,18],[5,5],[6,11],[11,14],[13,1],[15,1],[17,4]], expected: false },
];

testCases.forEach((test, index) => {
    const result = canFinish(test.numCourses, test.prerequisites);
    console.log(`Test Case ${index + 1}: ${result === test.expected ? 'Passed' : 'Failed'} (Expected: ${test.expected}, Got: ${result})`);
});
© d)zharii. sitemap