Skip to main content
DFS Pattern

What is DFS?

Depth-First Search explores as far as possible along each branch before backtracking. It uses a stack (explicit or via recursion) to track the path and is ideal for problems requiring complete path exploration.
Quick Recognition: If you need to explore all paths, find connected components, detect cycles, or perform tree traversals, think DFS!

Pattern Recognition Checklist

Use DFS When

  • Need to explore all paths
  • Finding connected components
  • Detecting cycles in graphs
  • Tree traversals (pre/in/post order)
  • Backtracking problems

Don't Use When

  • Finding shortest path (use BFS)
  • Level-order traversal needed
  • Path length matters more than existence
  • Graph is very deep (stack overflow risk)

When to Use

Tree Traversals

Preorder, inorder, postorder traversals

Path Finding

Finding all paths, checking path existence

Connected Components

Counting islands, detecting cycles

Backtracking

Permutations, combinations, subset generation

Pattern Variations

1. Tree DFS (Recursive)

def tree_dfs(root):
    """Basic tree DFS traversal"""
    if not root:
        return
    
    # Preorder: process before children
    print(root.val)
    
    tree_dfs(root.left)
    
    # Inorder: process between children (BST gives sorted order)
    
    tree_dfs(root.right)
    
    # Postorder: process after children

2. Graph DFS with Visited Set

def graph_dfs(graph, start):
    """DFS on adjacency list graph"""
    visited = set()
    result = []
    
    def dfs(node):
        if node in visited:
            return
        
        visited.add(node)
        result.append(node)
        
        for neighbor in graph[node]:
            dfs(neighbor)
    
    dfs(start)
    return result

3. Grid DFS (Island Problems)

def count_islands(grid):
    """Count connected components in a grid"""
    if not grid:
        return 0
    
    rows, cols = len(grid), len(grid[0])
    count = 0
    
    def dfs(r, c):
        if r < 0 or r >= rows or c < 0 or c >= cols:
            return
        if grid[r][c] != '1':
            return
        
        grid[r][c] = '0'  # Mark visited
        
        # Explore 4 directions
        dfs(r + 1, c)
        dfs(r - 1, c)
        dfs(r, c + 1)
        dfs(r, c - 1)
    
    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == '1':
                count += 1
                dfs(r, c)
    
    return count

4. DFS with Path Tracking

def find_all_paths(graph, start, end):
    """Find all paths from start to end"""
    all_paths = []
    
    def dfs(node, path):
        if node == end:
            all_paths.append(path[:])
            return
        
        for neighbor in graph[node]:
            if neighbor not in path:  # Avoid cycles
                path.append(neighbor)
                dfs(neighbor, path)
                path.pop()  # Backtrack
    
    dfs(start, [start])
    return all_paths

5. Iterative DFS with Stack

def iterative_dfs(graph, start):
    """DFS using explicit stack"""
    visited = set()
    stack = [start]
    result = []
    
    while stack:
        node = stack.pop()
        
        if node in visited:
            continue
        
        visited.add(node)
        result.append(node)
        
        # Add neighbors in reverse order for correct traversal
        for neighbor in reversed(graph[node]):
            if neighbor not in visited:
                stack.append(neighbor)
    
    return result

Classic Problems

Pattern: Grid DFS marking visited cellsKey: Sink the island as you explore it
Pattern: DFS with HashMap for visited/cloned nodesKey: Create clone first, then connect neighbors
Pattern: Cycle detection with coloring (white/gray/black)Key: Gray nodes indicate cycle in current path

Cycle Detection

def has_cycle_directed(graph, n):
    """Detect cycle in directed graph using DFS coloring"""
    WHITE, GRAY, BLACK = 0, 1, 2
    color = [WHITE] * n
    
    def dfs(node):
        color[node] = GRAY  # Currently exploring
        
        for neighbor in graph[node]:
            if color[neighbor] == GRAY:  # Back edge = cycle
                return True
            if color[neighbor] == WHITE and dfs(neighbor):
                return True
        
        color[node] = BLACK  # Fully explored
        return False
    
    for i in range(n):
        if color[i] == WHITE and dfs(i):
            return True
    
    return False

Common Mistakes

Avoid These Pitfalls:
  1. Stack overflow: Use iterative for deep recursion
  2. Forgetting visited check: Can cause infinite loops
  3. Not restoring state: Backtracking requires cleanup
  4. Wrong traversal order: Preorder vs postorder matters

Debugging Checklist

When your DFS solution fails:
1

Check Visited Tracking

Are you marking nodes as visited? Using set or boolean array?
2

Check Base Cases

What stops the recursion? Null node? Visited node?
3

Check Backtracking

Are you restoring state after exploring each path?
4

Check Graph Type

Directed or undirected? Affects cycle detection logic.
5

Check Order

Preorder, inorder, or postorder? Order matters for result.

DFS vs BFS Quick Reference

AspectDFSBFS
Data StructureStack / RecursionQueue
Path FindingAll pathsShortest path
SpaceO(h) heightO(w) width
Cycle DetectionYes (with coloring)Yes (undirected)
Topological SortYesYes (Kahn’s)
Best ForDeep explorationLevel-by-level

Interview Problems by Company

ProblemCompanyKey Concept
Max Depth of TreeAllBasic tree DFS
Same TreeAmazonParallel DFS
Path SumMetaRoot-to-leaf DFS
Invert Binary TreeGoogleTree modification
Symmetric TreeAmazonMirror DFS

Interview Tips

Script for interviews:
  1. “I’ll use DFS to explore all possibilities.”
  2. “I’ll maintain a visited set to avoid cycles.”
  3. “At each node, I’ll [do work] before/after visiting children.”
  4. “Base case is when [condition] is met.”
  5. “Time is O(V+E) for graphs, O(n) for trees.”
Interviewer SaysYou Should Think
”Find all paths”DFS with path tracking
”Check if path exists”Simple DFS
”Find connected components”DFS on each unvisited
”Detect cycle”DFS with coloring
”Topological order”DFS postorder reversal
Use Recursive When:
  • Tree problems (max depth is log n or limited)
  • Code clarity matters
  • Natural recursive structure
Use Iterative When:
  • Graph can be very deep (avoid stack overflow)
  • Need explicit control over stack
  • Performance is critical

Practice Problems

ProblemDifficultyLink
Number of IslandsMediumLeetCode 200
Clone GraphMediumLeetCode 133
Course ScheduleMediumLeetCode 207
Word SearchMediumLeetCode 79
Surrounded RegionsMediumLeetCode 130

Practice Roadmap

1

Day 1: Tree DFS

  • Solve: Max Depth, Path Sum, Same Tree
  • Focus: Basic recursive tree traversal
2

Day 2: Grid DFS

  • Solve: Number of Islands, Flood Fill
  • Focus: 4-directional traversal pattern
3

Day 3: Graph DFS

  • Solve: Clone Graph, Course Schedule
  • Focus: Visited tracking and cycle detection
4

Day 4: Advanced

  • Solve: Word Search, All Paths from Source
  • Focus: Backtracking in DFS
Interview Tip: When exploring all possibilities or finding paths, think DFS. When finding shortest path, think BFS.