Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Graph Algorithms
MediumLC 207

Course Schedule (LeetCode 207)

Target Frequency:GoogleAmazonMicrosoft

You have numCourses courses to complete, numbered 0 through numCourses - 1, and a list prerequisites where each entry [ai, bi] means course bi must be completed before course ai.

  • So an entry [0, 1] means course 1 has to be finished before course 0.

Decide whether every course can be completed at all — return true if a valid completion order exists, and false otherwise.

Worked Examples

Example 1
Input:numCourses = 2, prerequisites = [[1,0]]
Output:true
Explanation: With 2 courses total, course 1's only requirement is course 0, so taking course 0 then course 1 completes both.
Example 2
Input:numCourses = 2, prerequisites = [[1,0],[0,1]]
Output:false
Explanation: Course 1 needs course 0 done first, but course 0 also needs course 1 done first — neither can go first, so no order finishes both.

⚖️Formal Constraints & Bounds

  • 1 <= numCourses <= 2000

  • 0 <= prerequisites.length <= 5000

  • prerequisites[i].length == 2

  • 0 <= ai, bi < numCourses

  • All the pairs prerequisites[i] are unique.

Deep-Dive & Conceptual Insights

Core Insight

Directed cycle detection via in-degree tracking prevents deadlocks in linear time.

Real-World Scenario & Production Applications

Build system package dependency resolution (npm, bazel, make), compiler task DAG(Directed Acyclic Graph) scheduling.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Adjacency List & In-Degree Initialization

Construct directed graph from edge pairs and compute in-degree for every vertex: in_degree[v] = number of prerequisite dependencies.

Mathematical Recurrence / Code Invariant
adj = defaultdict(list)
in_degree = [0] * numCourses
for dest, src in prerequisites:
    adj[src].append(dest)
    in_degree[dest] += 1

Step-by-Step Execution Trace Table

Queue (0 In-Degree)Popped CourseDecremented DependentsIn-Degree State
[0]Course 0in_degree[1] -> 0in_degree[1]=0
[1]Course 1NoneCompleted
ResultCount = 2 == numCourses (2) -> True--
Trace InputnumCourses = 2, prerequisites = [[1,0]]
Expectedtrue
Queue (0 In-Degree)Popped CourseDecremented DependentsIn-Degree State
[0]Course 0in_degree[1] -> 0in_degree[1]=0
[1]Course 1NoneCompleted
ResultCount = 2 == numCourses (2) -> True--
Code Shape
Archetype Code Shape
Python
1WHITE, GREY, BLACK = 0, 1, 2
2state = [WHITE] * numCourses
3adj[prereq].append(course) for every pair
4def dfs(u):
5 state[u] = GREY
6 for v in adj[u]:
7 if state[v] == GREY:
8 return True
9 if state[v] == WHITE and dfs(v):
10 return True
11 state[u] = BLACK
12 return False
13for node in range(numCourses):
14 if state[node] == WHITE and dfs(node):
15 return False
16return True

Conceptual Narrative

Course Schedule models prerequisites as a directed graph: every course can be finished exactly when that graph has no directed cycle.

🧭 Conceptual Foundation & Pattern Intuition

An edge prereq -> course means "take prereq first". A cycle such as 0 -> 1 -> 0 means each course waits on the other, so neither can ever start. The Cycle Detection card finds such a loop with a depth-first search that gives every course one of three states: WHITE (not visited), GREY (on the current search path) and BLACK (finished, and known to reach no cycle).

🏟️ The Analogy: Following a Chain of Prerequisites With Your Finger

Start at a course and follow its arrows to the courses it unlocks, marking each course you pass as "in progress" (GREY). If your finger lands on a course that is already in progress, you have walked in a circle: that is a cycle. When every arrow out of a course has been followed without finding one, stamp the course "done" (BLACK). A later walk that reaches a done course can stop there, because nothing past it loops.

🔑 Why It Works

Only GREY means a cycle. Reaching a BLACK course again through a second path is safe, which is why a single visited flag is not enough.

  • Inverted prerequisite edge direction: Input [a, b] means 'take b before a' (b→ab \rightarrow ab→a). Creating edge a→ba \rightarrow ba→b inverts the dependency DAG(Directed Acyclic Graph) and yields erroneous schedules.

  • Ignoring disconnected graph components: Failing to seed the queue with ALL zero-in-degree nodes across all disjoint components leaves valid subgraphs unexplored.

  • Self-loops and parallel edges: A course listing itself as a prerequisite ([1, 1]) creates an immediate cycle of length 1. Ensure edge deduplication or proper in-degree accounting.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

Each pair [ai, bi] means course bi "must be completed before" course ai, and the question is "whether every course can be completed at all": the pairs are the edges of a directed graph, and a valid order exists exactly when that graph has no cycle. Only a yes/no is asked, not the order itself, so this is Cycle Detection with three colors.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

The GREY courses are exactly the courses on the current dfs path, and every course reachable from a BLACK course is BLACK and on no cycle. So a neighbour with state[v] == GREY is an edge back into the path (a cycle), a BLACK neighbour is skipped, and a WHITE one is searched.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • if state[v] == GREY, not "already visited": with one visited flag, a course reached again through a second path looks like a cycle, and numCourses = 3, prerequisites = [[1,0],[2,0],[2,1]] returns false instead of true.

  • state[u] = BLACK goes after the for v in adj[u] loop: a course marked finished on entry is skipped when a later edge comes back to it, so Example 2's cycle is never seen.

  • for node in range(numCourses): start a dfs at every course still WHITE, because a cycle can sit in a part of the graph that course 0 never reaches.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"The pairs form a directed graph, and every course can be finished exactly when that graph has no cycle, so I'd use Cycle Detection with three colors. Every course starts white. When my depth-first search enters a course it turns grey, meaning it is on the current path, and once all its neighbours are done it turns black. Reaching a grey course means an edge back into my own path, so that path plus the edge is a cycle and I return false. A black course can't lead to a cycle, so I skip it. The trap is using one visited flag: reaching a finished course again through a second path is not a cycle, so grey and black must stay different. I start a search from every white course, so no part of the graph is missed. It's O(V plus E) time and space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(V + E)

Look at the code: building adj reads each of the E pairs once, and state and adj are V-sized lists. dfs(u) is only called on a WHITE course and turns it GREY at once, so it runs at most once per course: at most V calls in total. Each call walks adj[u] once with O(1) work per entry (the GREY test and the WHITE test), so all the loops together read each pair once. The outer for node in range(numCourses) adds V constant-time checks. Total: O(V + E).

SPACE COMPLEXITY

O(V + E)

adj holds V lists with E entries in total, and state holds V colors. The recursion stack holds the GREY courses of the current path, at most V frames on a long chain. The output is one boolean.

Formal Recurrence Relation

T(V, E) = V + E (build adj) + V (dfs calls) + E (adjacency entries) + V (outer loop) = O(V + E)

Derivation Progression

Build the graph

state = [WHITE] * numCourses and adj: O(V); for course, prereq in prerequisites: O(E)

Each pair adds one edge prereq -> course to adj.

Each course is searched once

dfs(u) runs only when state[u] == WHITE and sets state[u] = GREY first: at most V calls

A course never goes back to WHITE, so no course is entered twice.

Each pair is read once

sum over dfs calls of len(adj[u]) = E, O(1) per entry

The for v in adj[u] loop runs once per call, and each entry costs one GREY test and one WHITE test.

Outer loop over every course

for node in range(numCourses): V checks

A course that is already GREY or BLACK costs one comparison; only a WHITE one opens a search.

Variable Definitions

VVV

Number of courses, numCourses

EEE

Number of prerequisite pairs, len(prerequisites)

Memory Architecture & Bounds

🟣 Call Stack

O(V): the GREY courses on the current path, V frames on a chain of courses

🔵 Auxiliary Heap

O(V + E): adj holds E entries in V lists, and state holds V colors

🟢 Output Space

O(1): one boolean

Boundary Best / Worst Cases

Best Case

O(V+E)O(V + E)O(V+E) to build adj; the search itself can stop early, when the first path it follows already closes a cycle.

Average Case

O(V+E)O(V + E)O(V+E).

Worst Case

O(V+E)O(V + E)O(V+E): no cycle, so every course is searched and every pair is read before return True.

Graph & Tree Traversal Frontier

Graph & Tree Traversal Frontier
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Scale signal: a separate walk from every course costs O(V⋅(V+E))O(V \cdot (V + E))O(V⋅(V+E)), about 2000⋅7000=1.4×1072000 \cdot 7000 = 1.4 \times 10^72000⋅7000=1.4×107 steps here, which still passes, but real dependency graphs (build targets, packages) have far more nodes, and only the one-pass Cycle Detection stays linear.

CONSTRAINTS & BOUNDS

V≤2000V \le 2000V≤2000 courses, E≤5000E \le 5000E≤5000 pairs: adj holds 5000 entries and state 2000 colors. A chain of 2000 courses makes the recursion 2000 frames deep, past CPython's default limit of 1000, so the depth, not the time, is what breaks first.

FAANG PRODUCTION TRAPS & EDGE CASES

Deep graphs: production code runs the same three colors on an explicit stack of (node, next neighbour) frames instead of recursion. A bare false is hard to act on: the GREY courses at the moment the cycle is found are the cycle's path, so keep them to report which dependencies form the loop.

Core Algorithmic State Invariants

  1. Example 2, Step by Step

prerequisites = [[1,0],[0,1]] gives adj[0] = [1] and adj[1] = [0]. dfs(0) sets state[0] = GREY and calls dfs(1), which sets state[1] = GREY and reads v = 0: state[0] == GREY, an edge back into the path 0 -> 1, so canFinish returns false.

  1. Edge Direction Doesn't Change the Answer

Reversing every pair turns each cycle into the same cycle walked backwards, so building adj[course].append(prereq) instead would return the same true or false. The direction only matters when the order itself must be returned.

Theory Context•Graph Algorithms
MediumLC 207

Course Schedule (LeetCode 207)

Target Frequency:GoogleAmazonMicrosoft

You have numCourses courses to complete, numbered 0 through numCourses - 1, and a list prerequisites where each entry [ai, bi] means course bi must be completed before course ai.

  • So an entry [0, 1] means course 1 has to be finished before course 0.

Decide whether every course can be completed at all — return true if a valid completion order exists, and false otherwise.

Worked Examples

Example 1
Input:numCourses = 2, prerequisites = [[1,0]]
Output:true
Explanation: With 2 courses total, course 1's only requirement is course 0, so taking course 0 then course 1 completes both.
Example 2
Input:numCourses = 2, prerequisites = [[1,0],[0,1]]
Output:false
Explanation: Course 1 needs course 0 done first, but course 0 also needs course 1 done first — neither can go first, so no order finishes both.

⚖️Formal Constraints & Bounds

  • 1 <= numCourses <= 2000

  • 0 <= prerequisites.length <= 5000

  • prerequisites[i].length == 2

  • 0 <= ai, bi < numCourses

  • All the pairs prerequisites[i] are unique.

Deep-Dive & Conceptual Insights

Core Insight

Directed cycle detection via in-degree tracking prevents deadlocks in linear time.

Real-World Scenario & Production Applications

Build system package dependency resolution (npm, bazel, make), compiler task DAG(Directed Acyclic Graph) scheduling.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Adjacency List & In-Degree Initialization

Construct directed graph from edge pairs and compute in-degree for every vertex: in_degree[v] = number of prerequisite dependencies.

Mathematical Recurrence / Code Invariant
adj = defaultdict(list)
in_degree = [0] * numCourses
for dest, src in prerequisites:
    adj[src].append(dest)
    in_degree[dest] += 1

Step-by-Step Execution Trace Table

Queue (0 In-Degree)Popped CourseDecremented DependentsIn-Degree State
[0]Course 0in_degree[1] -> 0in_degree[1]=0
[1]Course 1NoneCompleted
ResultCount = 2 == numCourses (2) -> True--
Trace InputnumCourses = 2, prerequisites = [[1,0]]
Expectedtrue
Queue (0 In-Degree)Popped CourseDecremented DependentsIn-Degree State
[0]Course 0in_degree[1] -> 0in_degree[1]=0
[1]Course 1NoneCompleted
ResultCount = 2 == numCourses (2) -> True--
Code Shape
Archetype Code Shape
Python
1WHITE, GREY, BLACK = 0, 1, 2
2state = [WHITE] * numCourses
3adj[prereq].append(course) for every pair
4def dfs(u):
5 state[u] = GREY
6 for v in adj[u]:
7 if state[v] == GREY:
8 return True
9 if state[v] == WHITE and dfs(v):
10 return True
11 state[u] = BLACK
12 return False
13for node in range(numCourses):
14 if state[node] == WHITE and dfs(node):
15 return False
16return True

Conceptual Narrative

Course Schedule models prerequisites as a directed graph: every course can be finished exactly when that graph has no directed cycle.

🧭 Conceptual Foundation & Pattern Intuition

An edge prereq -> course means "take prereq first". A cycle such as 0 -> 1 -> 0 means each course waits on the other, so neither can ever start. The Cycle Detection card finds such a loop with a depth-first search that gives every course one of three states: WHITE (not visited), GREY (on the current search path) and BLACK (finished, and known to reach no cycle).

🏟️ The Analogy: Following a Chain of Prerequisites With Your Finger

Start at a course and follow its arrows to the courses it unlocks, marking each course you pass as "in progress" (GREY). If your finger lands on a course that is already in progress, you have walked in a circle: that is a cycle. When every arrow out of a course has been followed without finding one, stamp the course "done" (BLACK). A later walk that reaches a done course can stop there, because nothing past it loops.

🔑 Why It Works

Only GREY means a cycle. Reaching a BLACK course again through a second path is safe, which is why a single visited flag is not enough.

  • Inverted prerequisite edge direction: Input [a, b] means 'take b before a' (b→ab \rightarrow ab→a). Creating edge a→ba \rightarrow ba→b inverts the dependency DAG(Directed Acyclic Graph) and yields erroneous schedules.

  • Ignoring disconnected graph components: Failing to seed the queue with ALL zero-in-degree nodes across all disjoint components leaves valid subgraphs unexplored.

  • Self-loops and parallel edges: A course listing itself as a prerequisite ([1, 1]) creates an immediate cycle of length 1. Ensure edge deduplication or proper in-degree accounting.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

Each pair [ai, bi] means course bi "must be completed before" course ai, and the question is "whether every course can be completed at all": the pairs are the edges of a directed graph, and a valid order exists exactly when that graph has no cycle. Only a yes/no is asked, not the order itself, so this is Cycle Detection with three colors.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

The GREY courses are exactly the courses on the current dfs path, and every course reachable from a BLACK course is BLACK and on no cycle. So a neighbour with state[v] == GREY is an edge back into the path (a cycle), a BLACK neighbour is skipped, and a WHITE one is searched.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • if state[v] == GREY, not "already visited": with one visited flag, a course reached again through a second path looks like a cycle, and numCourses = 3, prerequisites = [[1,0],[2,0],[2,1]] returns false instead of true.

  • state[u] = BLACK goes after the for v in adj[u] loop: a course marked finished on entry is skipped when a later edge comes back to it, so Example 2's cycle is never seen.

  • for node in range(numCourses): start a dfs at every course still WHITE, because a cycle can sit in a part of the graph that course 0 never reaches.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"The pairs form a directed graph, and every course can be finished exactly when that graph has no cycle, so I'd use Cycle Detection with three colors. Every course starts white. When my depth-first search enters a course it turns grey, meaning it is on the current path, and once all its neighbours are done it turns black. Reaching a grey course means an edge back into my own path, so that path plus the edge is a cycle and I return false. A black course can't lead to a cycle, so I skip it. The trap is using one visited flag: reaching a finished course again through a second path is not a cycle, so grey and black must stay different. I start a search from every white course, so no part of the graph is missed. It's O(V plus E) time and space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(V + E)

Look at the code: building adj reads each of the E pairs once, and state and adj are V-sized lists. dfs(u) is only called on a WHITE course and turns it GREY at once, so it runs at most once per course: at most V calls in total. Each call walks adj[u] once with O(1) work per entry (the GREY test and the WHITE test), so all the loops together read each pair once. The outer for node in range(numCourses) adds V constant-time checks. Total: O(V + E).

SPACE COMPLEXITY

O(V + E)

adj holds V lists with E entries in total, and state holds V colors. The recursion stack holds the GREY courses of the current path, at most V frames on a long chain. The output is one boolean.

Formal Recurrence Relation

T(V, E) = V + E (build adj) + V (dfs calls) + E (adjacency entries) + V (outer loop) = O(V + E)

Derivation Progression

Build the graph

state = [WHITE] * numCourses and adj: O(V); for course, prereq in prerequisites: O(E)

Each pair adds one edge prereq -> course to adj.

Each course is searched once

dfs(u) runs only when state[u] == WHITE and sets state[u] = GREY first: at most V calls

A course never goes back to WHITE, so no course is entered twice.

Each pair is read once

sum over dfs calls of len(adj[u]) = E, O(1) per entry

The for v in adj[u] loop runs once per call, and each entry costs one GREY test and one WHITE test.

Outer loop over every course

for node in range(numCourses): V checks

A course that is already GREY or BLACK costs one comparison; only a WHITE one opens a search.

Variable Definitions

VVV

Number of courses, numCourses

EEE

Number of prerequisite pairs, len(prerequisites)

Memory Architecture & Bounds

🟣 Call Stack

O(V): the GREY courses on the current path, V frames on a chain of courses

🔵 Auxiliary Heap

O(V + E): adj holds E entries in V lists, and state holds V colors

🟢 Output Space

O(1): one boolean

Boundary Best / Worst Cases

Best Case

O(V+E)O(V + E)O(V+E) to build adj; the search itself can stop early, when the first path it follows already closes a cycle.

Average Case

O(V+E)O(V + E)O(V+E).

Worst Case

O(V+E)O(V + E)O(V+E): no cycle, so every course is searched and every pair is read before return True.

Graph & Tree Traversal Frontier

Graph & Tree Traversal Frontier
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Scale signal: a separate walk from every course costs O(V⋅(V+E))O(V \cdot (V + E))O(V⋅(V+E)), about 2000⋅7000=1.4×1072000 \cdot 7000 = 1.4 \times 10^72000⋅7000=1.4×107 steps here, which still passes, but real dependency graphs (build targets, packages) have far more nodes, and only the one-pass Cycle Detection stays linear.

CONSTRAINTS & BOUNDS

V≤2000V \le 2000V≤2000 courses, E≤5000E \le 5000E≤5000 pairs: adj holds 5000 entries and state 2000 colors. A chain of 2000 courses makes the recursion 2000 frames deep, past CPython's default limit of 1000, so the depth, not the time, is what breaks first.

FAANG PRODUCTION TRAPS & EDGE CASES

Deep graphs: production code runs the same three colors on an explicit stack of (node, next neighbour) frames instead of recursion. A bare false is hard to act on: the GREY courses at the moment the cycle is found are the cycle's path, so keep them to report which dependencies form the loop.

Core Algorithmic State Invariants

  1. Example 2, Step by Step

prerequisites = [[1,0],[0,1]] gives adj[0] = [1] and adj[1] = [0]. dfs(0) sets state[0] = GREY and calls dfs(1), which sets state[1] = GREY and reads v = 0: state[0] == GREY, an edge back into the path 0 -> 1, so canFinish returns false.

  1. Edge Direction Doesn't Change the Answer

Reversing every pair turns each cycle into the same cycle walked backwards, so building adj[course].append(prereq) instead would return the same true or false. The direction only matters when the order itself must be returned.

© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•