0
0
DSA Typescriptprogramming

Cycle Detection in Directed Graph in DSA Typescript

Choose your learning style9 modes available
Mental Model
We want to find if there is a path that starts and ends at the same node following the direction of edges.
Analogy: Imagine a one-way street map where you want to check if you can start driving from a place and come back to it by following the one-way roads without reversing.
A -> B -> C
↑         ↓
└─────────
Dry Run Walkthrough
Input: Graph with edges: A->B, B->C, C->A (cycle exists)
Goal: Detect if the graph contains a cycle
Step 1: Start DFS from node A, mark A as visiting
visiting: {A}, visited: {}
Why: We begin exploring from A to find cycles
Step 2: From A, move to B, mark B as visiting
visiting: {A, B}, visited: {}
Why: Continue exploring neighbors to find cycle
Step 3: From B, move to C, mark C as visiting
visiting: {A, B, C}, visited: {}
Why: Keep going deeper to check for cycle
Step 4: From C, move to A which is already visiting
visiting: {A, B, C}, visited: {}
Why: Finding a node already in visiting means a cycle
Step 5: Cycle detected, stop search
Cycle found
Why: Cycle exists because we returned to a node still being explored
Result:
Cycle found
Annotated Code
DSA Typescript
class Graph {
  adjacencyList: Map<string, string[]>;

  constructor() {
    this.adjacencyList = new Map();
  }

  addEdge(src: string, dest: string) {
    if (!this.adjacencyList.has(src)) {
      this.adjacencyList.set(src, []);
    }
    this.adjacencyList.get(src)!.push(dest);
  }

  hasCycle(): boolean {
    const visiting = new Set<string>();
    const visited = new Set<string>();

    const dfs = (node: string): boolean => {
      if (visiting.has(node)) return true; // cycle found
      if (visited.has(node)) return false; // already checked

      visiting.add(node); // mark node as visiting

      for (const neighbor of this.adjacencyList.get(node) || []) {
        if (dfs(neighbor)) return true; // cycle detected in recursion
      }

      visiting.delete(node); // done exploring node
      visited.add(node); // mark node as visited
      return false;
    };

    for (const node of this.adjacencyList.keys()) {
      if (dfs(node)) return true; // cycle found
    }
    return false; // no cycle
  }
}

// Driver code
const graph = new Graph();
graph.addEdge('A', 'B');
graph.addEdge('B', 'C');
graph.addEdge('C', 'A');

console.log(graph.hasCycle() ? 'Cycle found' : 'No cycle');
if (visiting.has(node)) return true; // cycle found
Detect if current node is already in the visiting set, indicating a cycle
if (visited.has(node)) return false; // already checked
Skip nodes already fully explored to avoid repeated work
visiting.add(node); // mark node as visiting
Mark node as currently exploring to track recursion stack
for (const neighbor of this.adjacencyList.get(node) || []) { if (dfs(neighbor)) return true; }
Recursively visit all neighbors to detect cycles
visiting.delete(node); visited.add(node);
Mark node as fully explored after all neighbors checked
for (const node of this.adjacencyList.keys()) { if (dfs(node)) return true; }
Start DFS from each node to cover disconnected parts
OutputSuccess
Cycle found
Complexity Analysis
Time: O(V + E) because each node and edge is visited once during DFS
Space: O(V) for recursion stack and sets tracking node states
vs Alternative: Compared to naive repeated path checking which can be exponential, DFS with visiting sets is efficient and linear
Edge Cases
Empty graph
Returns no cycle since no nodes exist
DSA Typescript
for (const node of this.adjacencyList.keys()) { if (dfs(node)) return true; }
Graph with no edges
Returns no cycle because no paths to form cycles
DSA Typescript
for (const neighbor of this.adjacencyList.get(node) || []) { if (dfs(neighbor)) return true; }
Graph with self-loop (node points to itself)
Detects cycle immediately when visiting the node again
DSA Typescript
if (visiting.has(node)) return true; // cycle found
When to Use This Pattern
When you see a problem asking if a directed graph has a cycle, use DFS with visiting and visited sets to detect back edges indicating cycles.
Common Mistakes
Mistake: Not distinguishing between visiting and visited sets, causing false cycle detection
Fix: Use separate sets to track nodes currently in recursion (visiting) and fully explored (visited)
Summary
Detects if a directed graph contains a cycle by DFS tracking nodes in recursion stack.
Use when you need to know if a directed graph has cycles to avoid infinite loops or invalid states.
The key insight is that revisiting a node currently being explored means a cycle exists.