Given an integer n representing the number of courses labeled from 0 to n - 1, and an array of prerequisite pairs, determine if it's possible to enroll in all courses.

Each prerequisite is represented as a pair [a, b], indicating that course a must be taken before course b.

Example:

Image represents a directed graph illustrating a simple coding pattern, possibly related to data flow or state transitions.  The graph contains three nodes represented as circles.  One node is labeled 'θ' (theta), suggesting an initial state or input. Another node is labeled '1', and the third is labeled '2'.  A directed edge (arrow) connects the 'θ' node to the '1' node, indicating a unidirectional flow of information or a transition from 'θ' to '1'.  Two curved, bidirectional edges connect nodes '1' and '2', showing a reciprocal relationship or iterative interaction between these two states; information or control flows in both directions between '1' and '2'.  The overall structure suggests a sequence where an initial input ('θ') triggers a process ('1') that interacts iteratively with another component ('2') before potentially completing or transitioning to another state (not shown).
Input: n = 3, prerequisites = [[0, 1], [1, 2], [2, 1]]
Output: False

Explanation: Course 1 cannot be taken without first completing course 2 and, and vice versa.

Constraints:

In Plain English

New to this one? Start here. Below is the core idea in everyday language, plus a real-world analogy—then the sections that follow build on it with the formal reasoning, code, and step-by-step walkthroughs.

🎓 Real-world analogy: getting dressed

You must put on socks before shoes, and a shirt before a jacket. As long as the rules never form a loop (“A before B, B before C, C before A”), you can always find an order that obeys all of them. The trick: repeatedly do whatever has no unmet requirement left. If at some point everything remaining is still waiting on something else, you are stuck in a loop — impossible. Finding a valid order like this is a topological sort.

Intuition

Our goal is to check if enrollment into all courses is possible. Let’s start by identifying which situations make enrollment impossible.

Consider a simple case where there are just two courses. A scenario where enrollment into all courses is impossible occurs when each course is a prerequisite to the other, as graphically represented below:

Image represents a directed graph illustrating prerequisites, where `prerequisites = [[0, 1], [1, 0]]` defines the dependencies.  The graph consists of two nodes, represented by circles labeled '0' and '1'.  A directed edge, indicated by an arrow, connects node '0' to node '1', signifying that node '1' requires node '0' as a prerequisite.  Conversely, another directed edge connects node '1' to node '0', indicating a reciprocal dependency; node '0' also requires node '1'. The `prerequisites` array is a matrix where each inner array represents a dependency; `[0, 1]` means 1 depends on 0, and `[1, 0]` means 0 depends on 1.  This visual representation shows a cyclical dependency between the two nodes.

Here are a couple of impossible enrollment scenarios that could occur with prerequisites for three courses:

Image represents two directed graphs illustrating different prerequisite relationships.  Each graph contains three nodes labeled '0', '1', and '2', representing tasks or modules.  Arrows indicate dependencies; an arrow from node A to node B signifies that B requires A as a prerequisite. The left graph shows node 0 pointing to node 1, node 1 pointing to node 2, and node 2 pointing back to node 1, representing a cyclical dependency.  Above this graph, the text 'prerequisites = [[0,1] [1,2] [2,1]]' lists these dependencies as ordered pairs within a list of lists. The right graph shows node 0 pointing to node 1, node 1 pointing to node 2, and node 2 pointing to node 0, forming a different cyclical dependency.  Above this graph, the text 'prerequisites = [[2,0] [0,1] [1,2]]' similarly lists the dependencies as ordered pairs in a list of lists, reflecting the connections in the graph.  Both graphs visually represent different sets of prerequisites, highlighting how different dependency structures can be represented using directed graphs and their corresponding list representations.

What do we notice about these cases and their graphical representations? They both have a circular relationship: a cycle. This highlights that it’s impossible to enroll in all courses if there exists a circular dependency between courses. In other words, there must not be a cycle in the graphical representation of the courses for complete enrollment to be possible.

Another thing to note is that the first courses which can be completed are those without prerequisites. In the graphical representation, such a course would have no directed arrows pointing at them. Let’s refer to the number of directed edges incoming to a node as the in-degree of that node.


In the example below, each node's in-degree is displayed at the top right:

Image represents a directed graph illustrating prerequisites.  The graph consists of six nodes, numbered 0 through 5, represented as circles containing their respective numbers.  Arrows indicate directed edges showing dependencies; a directed edge from node *x* to node *y* means *y* depends on *x*.  The edges are labeled with small orange numbers, possibly representing weights or costs associated with the dependencies.  Specifically, node 0 has outgoing edges to nodes 1 and 2; node 1 has an outgoing edge to node 4; node 3 has an outgoing edge to node 2; node 2 has an outgoing edge to node 4; and node 4 has an outgoing edge to node 5. Above the graph, a Python list named `prerequisites` is defined, containing nested lists. Each nested list represents a dependency pair; for example, `[0,1]` indicates that node 1 depends on node 0.  The list explicitly defines all the dependencies shown in the graph.

Courses 0 and 3 have an in-degree of 0. So, let’s complete them first and remove them from the graph. By doing this, we reduce the number of prerequisites for courses 1 and 2. Course 1's indegree then decreases by 1, and course 2's indegree decreases by 2:

Image represents a directed graph illustrating a coding pattern transformation.  The left side shows an initial graph with two source nodes, labeled '0' (crossed out in red, indicating inactivity or removal) and '3' (also crossed out in red), each connected via grey, directed edges with weights (small orange numbers) to nodes '1' (weight 1) and '2' (weight 2) respectively. Node '1' and '2' are then connected to node '4' (weights 1 and 2 respectively), which in turn connects to node '5' (weight 1).  A dashed grey arrow separates the left and right graphs, indicating a transformation. The right side shows the transformed graph where nodes '0' and '3' are removed. Nodes '1' and '2' now directly connect to node '4' (weights 1 and 0 respectively), and node '4' still connects to node '5' (weight 1).  The weights on the edges represent some form of cost or value associated with the connection between nodes. The overall diagram depicts a simplification or optimization of the graph structure, potentially representing a refactoring step in a coding pattern.

Now, courses 1 and 2 have an in-degree of 0, which means we can enroll in them and remove them from the graph. Observe what happens when we continue the process of removing courses with an in-degree of 0 from the graph:

Image represents a directed graph illustrating a coding pattern, possibly related to dependency management or task execution.  The initial state shows three nodes: nodes '1' and '2' (grey, with a red strikethrough, indicating inactive or completed states) and node '4' (black, active).  Orange numbers (2 and 1) above the arrows indicate weights or priorities associated with the connections. Node '1' connects to node '4' with a weight of 2, and node '2' connects to node '4' with a weight of 0. Node '4' connects to node '5' (black, active) with a weight of 1.  The graph then transitions. Node '4' becomes inactive (grey with a red strikethrough), and a dashed arrow indicates a non-deterministic or asynchronous transition.  Node '5' remains active (black), but eventually also becomes inactive (grey with a red strikethrough) after a further transition indicated by a dashed arrow.  The overall diagram depicts a sequence of states, showing how dependencies are resolved and tasks are completed, potentially highlighting a scenario where a task (node 4) is completed before its dependent task (node 5) is executed, and then node 5 is completed.

By the end of this process, no courses remain, indicating it’s possible to enroll in all courses.


Now, consider an example with a cyclic dependency:

Image represents a directed graph illustrating prerequisites.  At the top, a Python list named `prerequisites` is defined, containing pairs of numbers representing dependencies: `[[0,1] [0,2] [3,2] [1,4] [2,4] [4,5] [5,2]]`.  The graph visually depicts these dependencies.  The graph consists of six nodes, numbered 0 through 5, represented as circles containing their respective numbers.  Directed edges, represented by arrows, connect the nodes, indicating the flow of dependencies.  For example, the edge from node 0 to node 1 (labeled with an orange '1' above the arrow) signifies that node 1 requires node 0 as a prerequisite. Similarly, node 2 requires node 0 (labeled '0'), node 2 requires node 3 (labeled '3'), node 4 requires node 1 (labeled '2'), node 4 requires node 2 (labeled '1'), node 5 requires node 4 (labeled '1'), and node 2 requires node 5 (a curved edge).  The numbers on the arrows correspond to the order of dependencies as listed in the `prerequisites` list.

Let's follow the same steps of removing courses with an in-degree of 0:

Image represents a directed graph illustrating a coding pattern, possibly related to graph transformations or optimization.  The initial graph shows nodes labeled 0 (greyed out and crossed with a red line, indicating it's inactive or removed), 1, 2, 3 (also greyed out and crossed), 4, and 5.  Orange numbers next to the edges represent weights or costs associated with each connection. Node 0 connects to node 1 with weight 1, and to node 2 with weight 0. Node 1 connects to node 4 with weight 2, and node 3 connects to node 2 with weight 3 and to node 4 with weight 1. Node 4 connects to node 5 with weight 1, and node 5 connects back to node 2 with weight 1.  The graph then undergoes a transformation, indicated by a dashed arrow.  In the transformed graph, nodes 0 and 3 are absent. The connections and weights between nodes 1, 2, 4, and 5 are rearranged. Node 1 is removed, and node 2 now connects to node 4 with weight 1. Node 4 connects to node 5 with weight 1, and node 5 connects back to node 2 with weight 1. A further transformation is shown, resulting in a final graph with the same nodes (1,2,4,5) but with slightly different connections and weights.  The transformations suggest a process of simplifying or optimizing the graph by removing nodes and adjusting edge weights.

Here, there is no way to progress because there aren't any courses with an in-degree of 0. When this happens, and there are still unvisited courses, a cyclic dependency exists, meaning enrolment is impossible.

Now, let’s identify a way to simulate the above process algorithmically.

Topological sort
The process we described above is essentially topological sorting, where vertices of a graph are sorted in such a way that for every directed edge u → v, node u comes before node v in the ordering of the topological sort.

An algorithm designed to perform topological sort is Kahn’s algorithm. Let’s see how we can use it to solve this problem.


The first step of Kahn's algorithm is to determine the in-degree of each course. This can be achieved by counting the number of times each course appears as a dependent in the prerequisite pairs: for a pair [a, b], course b depends on course a, so course b’s in-degree is incremented by one:

Image represents a directed acyclic graph (DAG) illustrating a topological sort problem, along with its prerequisites and in-degrees.  The upper part shows the DAG with six nodes (0, 1, 2, 3, 4, 5) represented as circles, connected by directed edges indicated by arrows.  The numbers on the arrows represent edge weights (not explicitly defined but implied). Node 0 points to node 1 and node 2; node 3 points to node 2; node 1 points to node 4; and node 4 points to node 5.  Below the DAG, a list named 'prerequisites' shows the dependencies as pairs of node indices: `[[0,1] [0,2] [3,2] [1,4] [2,4] [4,5]]`, indicating that node 1 requires node 0, node 2 requires node 0, node 2 requires node 3, node 4 requires node 1, node 4 requires node 2, and node 5 requires node 4.  Finally, a list named 'in_degrees' shows the number of incoming edges for each node: `[0 1 2 0 2 1]`, indicating that node 0 has 0 incoming edges, node 1 has 1, node 2 has 2, and so on.  Orange curved arrows connect the prerequisites pairs to their corresponding in-degree values in the `in_degrees` list, visually linking the dependencies to the count of incoming edges for each node.  The numbers below the `in_degrees` list (0, 1, 2, 3, 4, 5) correspond to the node indices.

Now, we want to process all the courses with an in-degree of 0 first. We can add these courses to a queue to be processed:

Image represents a directed acyclic graph (DAG) with five nodes (0, 1, 2, 3, 4) and edges connecting them, along with an accompanying illustration of a topological sort algorithm's execution.  The nodes are numbered 0 through 4 and are depicted as circles containing their respective numbers.  Edges are represented by arrows indicating direction; for example, node 0 points to node 1, and node 3 points to node 2.  Small orange numbers next to each edge indicate a weight or some associated value. The algorithm uses an `in_degrees` array, shown as `[0 1 2 0 2 1]`, representing the number of incoming edges for each node (node 0 has 0 incoming edges, node 1 has 1, and so on).  A `queue` array, initialized as `[0 3]`, stores nodes with zero incoming edges.  A light green highlight visually emphasizes the current state of the `in_degrees` array, showing that nodes 0 and 3 are initially in the queue because they have no incoming edges.  The graph's structure and the `in_degrees` and `queue` arrays together illustrate the data structures used in a topological sorting algorithm.

To begin processing, pop the first course from the queue: course 0. Then, for each course that has course 0 as a prerequisite (i.e., courses pointed to by course 0), reduce their in-degree by one:

Image represents a directed acyclic graph (DAG) illustrating a topological sort algorithm.  The DAG consists of five nodes (circles) numbered 1 through 5, with directed edges (arrows) indicating dependencies. Node 0 is shown in gray and crossed out, indicating it's a source node that has been processed.  A directed edge from node *i* to node *j* implies that node *j* depends on node *i*.  The edges are labeled with small numbers representing weights or counts (e.g., 1→0, 2→1).  To the right, an array named `in_degrees` is shown, representing the in-degree of each node (number of incoming edges).  Initially, `in_degrees` is [0, 1, 2, 0, 2, 1], with node 0's in-degree crossed out because it's processed.  Another array, `queue`, is shown as [0, 3], indicating nodes with an in-degree of 0 (ready to be processed).  The orange numbers on the nodes and edges likely represent the order of processing during the topological sort.  The graph visually demonstrates the flow of information and dependencies, with the arrays tracking the algorithm's progress.

If any of the courses have an in-degree of 0 after being decremented, add them to the queue. Course 1 now has an in-degree of 0, so we add it to the queue:

Image represents a directed acyclic graph (DAG) illustrating a topological sorting algorithm.  The DAG consists of five nodes (1, 2, 3, 4, 5) represented as circles containing their respective node numbers.  Directed edges, represented by arrows, connect nodes indicating the flow of information; the numbers next to the arrows represent edge weights. Node 3 points to node 2 (weight 1), node 1 points to node 4 (weight 2), and node 2 points to node 4 (weight 2). Finally, node 4 points to node 5 (weight 1). To the right, an array labeled 'in_degrees' shows the in-degree of each node (number of incoming edges): node 1 has 0, node 2 has 1, node 3 has 0, node 4 has 2, and node 5 has 1.  A highlighted element '0' in the 'in_degrees' array indicates a node with an in-degree of 0, ready for processing.  Below, an array labeled 'queue' shows the queue of nodes ready for processing, initially containing nodes 3 and 1.  The arrangement visually demonstrates the algorithm's steps: identifying nodes with zero in-degrees, adding them to a queue, processing them, and updating in-degrees accordingly.

We can continue the above process until the queue is empty, indicating that there aren't any more courses with an in-degree of 0.

Implementation

from typing import List
from collections import defaultdict, deque
    
def prerequisites(n: int, prerequisites: List[List[int]]) -> bool:
    graph = defaultdict(list)
    in_degrees = [0] * n
    # Represent the graph as an adjacency list and record the in-degree of each
    # course.
    for prerequisite, course in prerequisites:
        graph[prerequisite].append(course)
        in_degrees[course] += 1
    queue = deque()
    # Add all courses with an in-degree of 0 to the queue.
    for i in range(n):
        if in_degrees[i] == 0:
            queue.append(i)
    enrolled_courses = 0
    # Perform topological sort.
    while queue:
        node = queue.popleft()
        enrolled_courses += 1
        for neighbor in graph[node]:
            in_degrees[neighbor] -= 1
            # If the in-degree of a neighboring course becomes 0, add it to the queue.
            if in_degrees[neighbor] == 0:
                queue.append(neighbor)
    # Return true if we've successfully enrolled in all courses.
    return enrolled_courses == n
#include <vector>
#include <queue>
using namespace std;

bool prerequisites(int n, vector<vector<int>>& edges) {
    vector<vector<int>> graph(n);
    vector<int> indegree(n, 0);
    for (int i = 0; i < (int)edges.size(); i++) {
        int a = edges[i][0];
        int b = edges[i][1];
        graph[a].push_back(b);           // a before b
        indegree[b]++;
    }
    queue<int> q;
    for (int i = 0; i < n; i++) {
        if (indegree[i] == 0) {
            q.push(i);
        }
    }
    int taken = 0;
    while (!q.empty()) {
        int course = q.front();
        q.pop();
        taken++;
        for (int b : graph[course]) {
            indegree[b]--;               // one prerequisite satisfied
            if (indegree[b] == 0) {
                q.push(b);               // now ready to take
            }
        }
    }
    return taken == n;
}
boolean prerequisites(int n, int[][] edges) {
    List<List<Integer>> graph = new ArrayList<>();
    for (int i = 0; i < n; i++) {
        graph.add(new ArrayList<>());
    }
    int[] indegree = new int[n];
    for (int[] e : edges) {
        graph.get(e[0]).add(e[1]);       // a before b
        indegree[e[1]]++;
    }
    Queue<Integer> q = new LinkedList<>();
    for (int i = 0; i < n; i++) {
        if (indegree[i] == 0) {
            q.add(i);
        }
    }
    int taken = 0;
    while (!q.isEmpty()) {
        int course = q.poll();
        taken++;
        for (int b : graph.get(course)) {
            indegree[b]--;               // one prerequisite satisfied
            if (indegree[b] == 0) {
                q.add(b);                // now ready to take
            }
        }
    }
    return taken == n;
}

Complexity Analysis

Time complexity: The time complexity of prerequisites is O(n+e)O(n+e) where ee denotes the number of edges derived from the prerequisites array. Here’s why:

Space complexity: The space complexity is O(n+e)O(n+e), since the adjacency list takes up O(n+e)O(n+e) space, while the in_degrees array and queue each take up O(n)O(n) space.

Dry Runs

Two step-by-step walkthroughs of the algorithm below: a valid case (the expected happy path) and an invalid / edge case that exercises the tricky parts. Each step shows the program state as the code runs—the same steps apply to Python, C++ and Java.

✅ Dry run 1 (valid) — a diamond of dependencies → True.

n = 4, edges = [[0,1],[0,2],[1,3],[2,3]]. Arrows mean “must be taken before.” In the diagrams: green = ready (in-degree 0), gray = still blocked, faded = already taken.

Step 1. Seed. In-degrees are 0:0, 1:1, 2:1, 3:2. Only course 0 has in-degree 0, so it is the only one ready. queue = [0].
Step 2. Pop 0. Taking 0 unlocks 1 and 2 — both drop to in-degree 0 and join the queue. taken = 1.
Step 3. Pop 1 then 2. Popping 1 drops 3 to in-degree 1 (still blocked); popping 2 drops it to 0, so 3 becomes ready. taken = 3.
Step 4. Pop 3. Nothing left to unlock. taken = 4 = n → a full order exists → True.

The same run as a math table

Pop a ready course, decrement what it unlocks, enqueue anything that hits 0.
poptakendecrement → new zero-in-degreequeue after
011→0, 2→0 (both ready)[1, 2]
123→1 (not ready yet)[2]
233→0 (ready)[3]
34nothing left[] → taken 4 == n

taken = 4 = n → True (a valid order is 0, 1, 2, 3). ✓

❌ Dry run 2 (invalid) — a 3-cycle → False.

n = 3, edges = [[0,1],[1,2],[2,0]]. Every course needs another first — a directed loop.

Step 1. Seed. Every course depends on another, so every in-degree is 1 — no course has in-degree 0 and the queue starts empty.
Step 2. Loop. With an empty queue the while-loop never runs, so taken stays 0. 0 ≠ 3 → the cycle can never be ordered → False.

The same run as a math table

No course starts ready, so the queue is empty from the start.
stepactiontaken
seed queueno course has in-degree 0 → queue is empty0
loopwhile-loop never runs; taken stays 0 ≠ 30 → return False

A cycle keeps every in-degree above 0, so taken can never reach n. ✗

The same steps in each language

The queue is seeded with deque(i for i in range(n) if indegree[i] == 0). We count taken and compare to n at the end — no explicit cycle search needed.

Same steps. Build the adjacency list as a vector<vector<int>>; seed a queue<int> with the zero-in-degree courses in a loop.

Same steps. Build a List<List<Integer>> adjacency list and a LinkedList queue; compare taken == n.