Quant Memo
Core

Graph Traversal — BFS and DFS

The two ways to walk a graph — breadth-first (level by level, with a queue) and depth-first (as deep as possible, with a stack). Both visit everything in linear time; which you reach for depends on whether you need shortest hops or deep structure.

Prerequisites: Big-O Complexity, Hash Maps and Sets

A graph is just a set of things (nodes, or vertices) and connections between them (edges). Cities and roads, functions and their callers, instruments and their correlations — anything you can draw as dots joined by lines is a graph. Almost every graph question starts by walking the graph: visiting every node reachable from a starting point. There are exactly two disciplined ways to do that walk, and knowing when to use each is the whole game.

Breadth-first search (BFS) explores in rings: first the start node, then everything one step away, then everything two steps away, and so on. It uses a queue (first-in, first-out). Depth-first search (DFS) plunges: follow one edge as far as it goes, hit a dead end, back up, and try the next branch. It uses a stack (last-in, first-out), which recursion gives you for free.

Complexity

Both visit every node once and look at every edge once, so both run in

O(V+E),O(V + E),

where VV is the number of nodes and EE the number of edges. Space is O(V)O(V) for the visited-set plus the queue or the recursion stack. Neither is "faster" in the big-O sense — they differ in order of visitation, and that order is what you exploit.

A B C D E F layer 0 layer 1 layer 2 layer 3
Starting from A, BFS visits nodes in layers by distance: A (0), then B and C (1), then D and E (2), then F (3). The layer number is the shortest number of edges from A — which is exactly why BFS finds shortest paths in an unweighted graph.

Reading the picture

From the diagram, BFS from A visits A → B, C → D, E → F — strictly by distance. DFS from A might visit A → B → D → F → E → C, diving down one branch before exploring the next. Same six nodes, completely different order.

from collections import deque

def bfs(graph, start):
    seen, q, order = {start}, deque([start]), []
    while q:
        node = q.popleft()          # FIFO — this makes it breadth-first
        order.append(node)
        for nbr in graph[node]:
            if nbr not in seen:
                seen.add(nbr)
                q.append(nbr)
    return order

def dfs(graph, node, seen=None, order=None):
    seen = seen or set(); order = order or []
    seen.add(node); order.append(node)
    for nbr in graph[node]:
        if nbr not in seen:
            dfs(graph, nbr, seen, order)   # recursion = the stack
    return order

The only structural difference between the two: BFS pops from the front of a queue, DFS pops from the top of a stack (or recurses). Swap the container and you swap the algorithm.

BFS = queue = shortest path in an unweighted graph. The first time BFS reaches a node, it does so by the fewest possible edges, so the layer number is the shortest distance. DFS = stack/recursion = go deep — the tool for connectivity, cycle detection, and topological ordering.

Where it shows up in quant-dev interviews

Grid problems ("number of islands," "shortest path in a maze") are BFS/DFS in disguise — the grid is a graph where each cell links to its neighbors. On a quant desk the same traversals do real work: resolving a dependency graph of calculations in the right order (topological sort via DFS), finding connected clusters in a correlation network (Markov Chains and correlation-clustering ideas ride on this), and detecting cycles in an order-routing or settlement graph. The interview favorite is "detect a cycle" — DFS with a recursion-stack marker — and its finance cousin, spotting an arbitrage loop, which is the same cycle hunt on a graph of exchange rates.

The number-one bug is forgetting the visited set, which sends the traversal into an infinite loop the moment the graph has a cycle. Mark a node when you enqueue it in BFS (not when you dequeue it), or you'll add duplicates to the queue and blow up the running time.

When the graph has weighted edges, plain BFS no longer finds the cheapest path — swap the queue for a priority queue and you have Dijkstra's algorithm. That upgrade is why Heaps and Priority Queues so often appears in the same breath as graph traversal.

Master the queue-vs-stack distinction, always guard with a visited set, and remember BFS-for-shortest-hops. That trio answers the large majority of graph questions you'll be handed.

Related concepts

Practice in interviews

Further reading

  • Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms (Elementary Graph Algorithms)
  • Sedgewick & Wayne, Algorithms, 4th ed. (Graphs)
ShareTwitterLinkedIn