Bird
Raised Fist0
DSA Typescriptprogramming~15 mins

Cycle Detection in Directed Graph in DSA Typescript - Deep Dive

Choose your learning style10 modes available
Overview - Cycle Detection in Directed Graph
What is it?
Cycle detection in a directed graph is the process of finding if there is a path that starts and ends at the same node following the direction of edges. In simple terms, it checks if you can start from a node and come back to it by following arrows in the graph. This is important because cycles can cause problems in tasks like scheduling or dependency management. Detecting cycles helps us know if a process can get stuck in a loop.
Why it matters
Without cycle detection, systems like task schedulers or package managers might try to complete tasks that depend on each other endlessly, causing programs to freeze or crash. For example, if task A depends on task B, and task B depends on task A, the system will never finish. Cycle detection prevents these infinite loops and helps maintain reliable and efficient systems.
Where it fits
Before learning cycle detection, you should understand what graphs are, especially directed graphs, and how to represent them using adjacency lists or matrices. After mastering cycle detection, you can learn about topological sorting, which requires a cycle-free graph, and advanced graph algorithms like strongly connected components.
Mental Model
Core Idea
Cycle detection in a directed graph finds if following arrows from a node can lead back to the same node, forming a loop.
Think of it like...
Imagine a one-way street map of a city. Cycle detection is like checking if you can start driving from a street, follow only one-way roads, and eventually return to the same street without breaking any traffic rules.
Directed Graph Example:

  A -> B -> C
       ↑    ↓
       E ← D

Cycle: B -> C -> D -> E -> B
Build-Up - 7 Steps
1
FoundationUnderstanding Directed Graphs
🤔
Concept: Learn what directed graphs are and how edges have direction.
A directed graph consists of nodes connected by edges that have a direction, like arrows pointing from one node to another. For example, if there is an edge from node A to node B, you can go from A to B but not necessarily from B to A. This direction matters when checking paths and cycles.
Result
You can represent relationships where order matters, such as task dependencies or web page links.
Understanding direction in edges is crucial because cycles depend on following these directions, not just connections.
2
FoundationGraph Representation Basics
🤔
Concept: Learn how to store directed graphs using adjacency lists.
An adjacency list stores each node with a list of nodes it points to. For example: const graph = { A: ['B'], B: ['C'], C: ['D'], D: ['E'], E: ['B'] }; This shows edges like A->B, B->C, etc. This structure makes it easy to explore neighbors.
Result
You can efficiently find all nodes reachable from any node.
Choosing the right representation simplifies traversal and cycle detection algorithms.
3
IntermediateDepth-First Search (DFS) Traversal
🤔
Concept: Use DFS to explore nodes deeply before backtracking.
DFS starts at a node and explores as far as possible along each branch before backtracking. It uses a stack (often via recursion) to remember where to return. For example, starting at A, DFS visits B, then C, then D, then E, and backtracks when no new nodes are found.
Result
You get a path exploring nodes deeply, which helps detect cycles by noticing revisits.
DFS's deep exploration naturally reveals cycles when a node is revisited in the current path.
4
IntermediateDetecting Cycles Using DFS States
🤔Before reading on: do you think revisiting any visited node means a cycle? Commit to yes or no.
Concept: Track node states during DFS to detect cycles precisely.
Each node can be in one of three states: - Not visited - Visiting (currently in recursion stack) - Visited (fully explored) If during DFS you reach a node marked 'Visiting', it means a cycle exists because you returned to a node still being explored.
Result
You can detect cycles exactly by checking if a node is in the current path.
Knowing the difference between nodes fully explored and nodes in the current path prevents false cycle detection.
5
IntermediateImplementing Cycle Detection in TypeScript
🤔Before reading on: do you think marking nodes as 'visited' alone is enough to detect cycles in directed graphs? Commit to yes or no.
Concept: Write code that uses DFS with node states to find cycles.
Example TypeScript code: function hasCycle(graph: Record): boolean { const visited = new Set(); const visiting = new Set(); function dfs(node: string): boolean { if (visiting.has(node)) return true; // cycle found if (visited.has(node)) return false; // already checked visiting.add(node); for (const neighbor of graph[node] || []) { if (dfs(neighbor)) return true; } visiting.delete(node); visited.add(node); return false; } for (const node in graph) { if (dfs(node)) return true; } return false; } // Using the graph from before: // hasCycle(graph) returns true because of cycle B->C->D->E->B
Result
The function returns true if a cycle exists, false otherwise.
Implementing state tracking in code directly maps the theory to practice, making cycle detection reliable.
6
AdvancedHandling Disconnected Graphs and Multiple Components
🤔Before reading on: do you think checking cycle from one node is enough for the whole graph? Commit to yes or no.
Concept: Cycle detection must consider all nodes, including disconnected parts.
Graphs can have multiple disconnected parts. To detect cycles anywhere, run DFS from every node not yet visited. This ensures no cycle is missed in isolated components.
Result
Cycle detection becomes complete and accurate for any graph shape.
Understanding graph connectivity prevents missing cycles hidden in isolated sections.
7
ExpertOptimizing Cycle Detection with Tarjan's Algorithm
🤔Before reading on: do you think simple DFS cycle detection can find all strongly connected components? Commit to yes or no.
Concept: Tarjan's algorithm finds strongly connected components (SCCs) efficiently, which helps detect cycles and more.
Tarjan's algorithm uses DFS with timestamps and a stack to find SCCs--groups of nodes where each node is reachable from any other. Any SCC with more than one node or a self-loop indicates a cycle. This method detects cycles and reveals graph structure in one pass, useful in advanced applications.
Result
You get all cycles grouped as SCCs, enabling deeper graph analysis.
Knowing Tarjan's algorithm extends cycle detection to structural graph insights, valuable in complex systems.
Under the Hood
Cycle detection uses DFS to explore nodes and edges deeply. It tracks nodes currently in the recursion stack (visiting) to detect back edges--edges pointing to ancestors in the DFS tree. These back edges indicate cycles. The algorithm marks nodes as visited once fully explored to avoid repeated work. Internally, sets or arrays track node states, and recursion manages traversal order.
Why designed this way?
DFS-based cycle detection is efficient and simple, running in linear time relative to nodes and edges. It leverages the natural recursion stack to track the current path, avoiding extra data structures. Alternatives like BFS are less straightforward for cycle detection in directed graphs. Tarjan's algorithm was designed to find SCCs in one pass, improving on naive methods by combining cycle detection with component identification.
Cycle Detection Flow:

Start DFS at node
  ↓
Mark node as Visiting
  ↓
For each neighbor:
  ├─ If neighbor Visiting -> Cycle found
  ├─ Else if neighbor Not Visited -> DFS(neighbor)
  ↓
Mark node as Visited
  ↓
Backtrack

States:
[Not Visited] -> [Visiting] -> [Visited]
Myth Busters - 3 Common Misconceptions
Quick: Does revisiting any visited node always mean a cycle? Commit yes or no.
Common Belief:If a node is visited again during DFS, it means there is a cycle.
Tap to reveal reality
Reality:Revisiting a node that is already fully visited (not in current path) does not mean a cycle; only revisiting nodes currently being visited (in recursion stack) indicates a cycle.
Why it matters:Mistaking revisits to fully visited nodes as cycles causes false positives, leading to incorrect conclusions about graph structure.
Quick: Is cycle detection in undirected graphs the same as in directed graphs? Commit yes or no.
Common Belief:Cycle detection works the same way for both directed and undirected graphs.
Tap to reveal reality
Reality:Cycle detection differs; in undirected graphs, revisiting any visited node except the immediate parent indicates a cycle, while in directed graphs, only back edges to nodes in the current path count.
Why it matters:Using the wrong method can miss cycles or falsely detect them, causing bugs in applications.
Quick: Can cycle detection be done by just marking nodes visited once? Commit yes or no.
Common Belief:Marking nodes as visited once is enough to detect cycles.
Tap to reveal reality
Reality:You must track nodes currently in the recursion stack separately; visited alone is insufficient to detect cycles in directed graphs.
Why it matters:Ignoring the recursion stack state leads to missing cycles, especially in complex graphs.
Expert Zone
1
Cycle detection algorithms can be adapted to detect cycles in weighted graphs by combining with shortest path algorithms.
2
In large graphs, iterative DFS with explicit stacks can avoid call stack overflow compared to recursive DFS.
3
Tarjan's algorithm not only detects cycles but also identifies strongly connected components, which is useful for optimizing graph processing.
When NOT to use
Cycle detection is not needed in undirected graphs if only connectivity matters; use Union-Find for cycle detection there. For dynamic graphs with frequent changes, incremental cycle detection algorithms or data structures like dynamic connectivity are better.
Production Patterns
Cycle detection is used in build systems to detect circular dependencies, in package managers to prevent dependency loops, and in compilers to check for infinite recursion or cyclic inheritance. It is also a key step before topological sorting in task scheduling.
Connections
Topological Sorting
Cycle detection is a prerequisite step before topological sorting.
Knowing if a graph has cycles tells you if topological sorting is possible, since cycles prevent a linear order.
Strongly Connected Components
Cycle detection can be extended to find strongly connected components using Tarjan's algorithm.
Understanding cycles helps reveal groups of nodes tightly linked, which is important in network analysis and optimization.
Deadlock Detection in Operating Systems
Cycle detection in resource allocation graphs identifies deadlocks.
Recognizing cycles in directed graphs helps detect when processes are stuck waiting on each other, a critical problem in OS design.
Common Pitfalls
#1Confusing visited nodes with nodes in the current path.
Wrong approach:function dfs(node) { if (visited.has(node)) return true; // cycle detected incorrectly visited.add(node); for (const neighbor of graph[node]) { if (dfs(neighbor)) return true; } return false; }
Correct approach:function dfs(node) { if (visiting.has(node)) return true; // cycle detected correctly if (visited.has(node)) return false; visiting.add(node); for (const neighbor of graph[node]) { if (dfs(neighbor)) return true; } visiting.delete(node); visited.add(node); return false; }
Root cause:Not distinguishing between nodes currently being explored and nodes fully explored leads to false cycle detection.
#2Running DFS from only one node in a disconnected graph.
Wrong approach:if (dfs(startNode)) return true; else return false;
Correct approach:for (const node in graph) { if (!visited.has(node)) { if (dfs(node)) return true; } } return false;
Root cause:Assuming the graph is connected causes missed cycles in isolated components.
#3Using BFS instead of DFS for cycle detection in directed graphs.
Wrong approach:function bfsCycleDetection() { // BFS does not track recursion stack, so can't detect cycles properly }
Correct approach:Use DFS with recursion stack tracking as shown in previous steps.
Root cause:BFS lacks the natural recursion stack needed to detect back edges indicating cycles.
Key Takeaways
Cycle detection in directed graphs finds loops by tracking nodes currently being explored during DFS.
Using three states--unvisited, visiting, and visited--prevents false cycle detection and ensures accuracy.
Cycle detection is essential before tasks like topological sorting or deadlock detection to avoid infinite loops.
Tarjan's algorithm extends cycle detection to find strongly connected components, revealing deeper graph structure.
Understanding graph connectivity and running DFS from all nodes ensures no cycle is missed in disconnected graphs.

Practice

(1/5)
1. What does cycle detection in a directed graph help us find?
easy
A. Whether there is a path that starts and ends at the same node following edge directions
B. The shortest path between two nodes
C. The total number of nodes in the graph
D. The maximum degree of any node

Solution

  1. Step 1: Understand cycle in directed graph

    A cycle means a path that starts and ends at the same node following the direction of edges.
  2. Step 2: Identify what cycle detection finds

    Cycle detection checks if such a path exists in the graph.
  3. Final Answer:

    Whether there is a path that starts and ends at the same node following edge directions -> Option A
  4. Quick Check:

    Cycle detection = finding directed loops [OK]
Hint: Cycle means path loops back following edge directions [OK]
Common Mistakes:
  • Confusing cycle with shortest path
  • Ignoring edge directions
  • Thinking cycle means any repeated node without direction
2. Which TypeScript data structure is commonly used to track nodes currently in recursion stack during cycle detection?
easy
A. Queue
B. Array
C. Map
D. Set

Solution

  1. Step 1: Identify recursion stack tracking

    We need to quickly check if a node is in the current recursion path.
  2. Step 2: Choose data structure for fast membership check

    A Set allows O(1) membership check, ideal for tracking nodes in recursion stack.
  3. Final Answer:

    Set -> Option D
  4. Quick Check:

    Recursion stack tracking uses Set [OK]
Hint: Use Set for fast membership check in recursion stack [OK]
Common Mistakes:
  • Using Array and checking membership with loops (slow)
  • Using Queue which is FIFO, not suitable here
  • Using Map without key-value need
3. What will be the output of the following TypeScript code snippet detecting cycle in a directed graph?
const graph = new Map([
  [1, [2]],
  [2, [3]],
  [3, [1]]
]);

function hasCycle(node: number, visited: Set<number>, recStack: Set<number>): boolean {
  if (!visited.has(node)) {
    visited.add(node);
    recStack.add(node);
    for (const neighbor of graph.get(node) || []) {
      if (!visited.has(neighbor) && hasCycle(neighbor, visited, recStack)) {
        return true;
      } else if (recStack.has(neighbor)) {
        return true;
      }
    }
  }
  recStack.delete(node);
  return false;
}

console.log(hasCycle(1, new Set(), new Set()));
medium
A. false
B. undefined
C. true
D. Error

Solution

  1. Step 1: Analyze graph edges

    Graph edges: 1->2, 2->3, 3->1 form a cycle.
  2. Step 2: Trace recursion and cycle detection

    Starting at 1, recursion visits 2, then 3, then back to 1 which is in recursion stack, so returns true.
  3. Final Answer:

    true -> Option C
  4. Quick Check:

    Cycle exists in 1->2->3->1 path [OK]
Hint: Cycle if recursion stack revisits a node [OK]
Common Mistakes:
  • Ignoring recursion stack check
  • Assuming no cycle if visited is true
  • Confusing visited and recursion stack sets
4. Identify the bug in this TypeScript cycle detection function for a directed graph:
function detectCycle(node: number, visited: Set<number>, recStack: Set<number>): boolean {
  if (visited.has(node)) {
    return false;
  }
  visited.add(node);
  recStack.add(node);
  for (const neighbor of graph.get(node) || []) {
    if (detectCycle(neighbor, visited, recStack)) {
      return true;
    }
  }
  recStack.delete(node);
  return false;
}
medium
A. Missing check if neighbor is in recursion stack to detect cycle
B. Visited set is not updated correctly
C. recStack is never cleared
D. Function does not return boolean

Solution

  1. Step 1: Review cycle detection logic

    Cycle detection requires checking if neighbor is already in recursion stack to detect back edge.
  2. Step 2: Identify missing condition

    The code calls recursion but does not check if neighbor is in recStack, so misses cycles.
  3. Final Answer:

    Missing check if neighbor is in recursion stack to detect cycle -> Option A
  4. Quick Check:

    Check recStack for back edges to detect cycles [OK]
Hint: Always check recursion stack for back edges [OK]
Common Mistakes:
  • Skipping recursion stack membership check
  • Confusing visited and recursion stack roles
  • Not removing node from recursion stack after recursion
5. Given a directed graph represented as adjacency list:
const graph = new Map([
  [1, [2, 3]],
  [2, [4]],
  [3, [4, 5]],
  [4, [6]],
  [5, [3]],
  [6, []]
]);
Which nodes are part of at least one cycle?
hard
A. [1, 2, 4, 6]
B. [3, 5]
C. [2, 4, 6]
D. [1, 3, 5]

Solution

  1. Step 1: Analyze edges for cycles

    Edges: 3->5 and 5->3 form a cycle between nodes 3 and 5.
  2. Step 2: Check other nodes

    Nodes 1, 2, 4, 6 do not form cycles as no back edges lead to them.
  3. Final Answer:

    [3, 5] -> Option B
  4. Quick Check:

    Cycle nodes = 3 and 5 only [OK]
Hint: Look for back edges forming loops between nodes [OK]
Common Mistakes:
  • Including nodes not in any cycle
  • Missing nodes that form cycles
  • Confusing path with cycle
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard