Depth-First Search

See DFS dive deep into each branch before backtracking through the graph.

Time O(V + E) · Space O(V)

Depth First Search Operations

Node
Edge
→
Run
Start
Target
100%
Ctrl + scroll to zoom

Implementation

Python
from typing import Dict, Set, List, Any


def dfs(graph: Dict[Any, Set[Any]], start: Any) -> List[Any]:
    """
    Perform DFS traversal starting from a given node (iterative).

    Args:
        graph: Adjacency list representation {node: {neighbors}}
        start: Starting node for traversal

    Returns:
        List of nodes in DFS order (depth-first)

    Raises:
        ValueError: If start node is not in graph
    """
    if start not in graph:
        raise ValueError(f"Start node '{start}' not in graph")

    visited = {start}
    stack = [start]  # Stack instead of queue - the key difference from BFS
    order = []

    while stack:
        node = stack.pop()  # LIFO: pop from end - O(1)
        order.append(node)

        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                stack.append(neighbor)

    return order


def dfs_recursive(graph: Dict[Any, Set[Any]], start: Any) -> List[Any]:
    """
    Perform DFS traversal using recursion (uses call stack implicitly).

    Args:
        graph: Adjacency list representation
        start: Starting node

    Returns:
        List of nodes in DFS order
    """
    if start not in graph:
        raise ValueError(f"Start node '{start}' not in graph")

    order = []
    visited = set()

    def _dfs(node: Any) -> None:
        visited.add(node)
        order.append(node)

        for neighbor in graph[node]:
            if neighbor not in visited:
                _dfs(neighbor)

    _dfs(start)
    return order


def dfs_find_path(graph: Dict[Any, Set[Any]], start: Any, target: Any) -> List[Any] | None:
    """
    Find a path between two nodes using DFS.

    Note: Unlike BFS, DFS does NOT guarantee the shortest path.
    It finds ANY valid path, which may be longer than optimal.

    Args:
        graph: Adjacency list representation
        start: Starting node
        target: Target node to reach

    Returns:
        List of nodes representing a path, or None if no path exists
    """
    if start not in graph:
        raise ValueError(f"Start node '{start}' not in graph")
    if target not in graph:
        raise ValueError(f"Target node '{target}' not in graph")

    if start == target:
        return [start]

    visited = {start}
    stack = [(start, [start])]  # (node, path_to_node)

    while stack:
        node, path = stack.pop()  # LIFO: pop from end

        for neighbor in graph[node]:
            if neighbor not in visited:
                new_path = path + [neighbor]

                if neighbor == target:
                    return new_path

                visited.add(neighbor)
                stack.append((neighbor, new_path))

    return None  # No path found


# Example usage and demonstration
if __name__ == "__main__":
    # Example graph (undirected, represented with bidirectional edges)
    #
    #       A
    #      / \
    #     B   C
    #    / \   \
    #   D   E   F
    #
    example_graph = {
        "A": {"B", "C"},
        "B": {"A", "D", "E"},
        "C": {"A", "F"},
        "D": {"B"},
        "E": {"B"},
        "F": {"C"},
    }

    print("DFS Traversal (iterative):", dfs(example_graph, "A"))
    print("DFS Traversal (recursive):", dfs_recursive(example_graph, "A"))
    print("DFS Path A->F:", dfs_find_path(example_graph, "A", "F"))
About Depth-First Search

How Depth-First Search Works

Depth-first search (DFS) explores a graph by going as deep as possible along each branch before backtracking. It uses a stack (or recursion) to remember which nodes to visit next. DFS is the foundation for many graph algorithms.

Start at the source node, mark it as visited. For each unvisited neighbor, recursively perform DFS. Backtrack when all neighbors have been visited. The implicit or explicit stack tracks the traversal path.

Complexity

Time
O(V + E)
Space
O(V)

When to Use Depth-First Search

  • Cycle detection in directed and undirected graphs
  • Topological sorting of directed acyclic graphs
  • Solving mazes and pathfinding puzzles