← Articles

BFS vs DFS: The Decision Framework for Coding Interviews

BFS and DFS are the two ways to walk a graph or a tree, and the choice between them comes down to one question: does the problem want the shortest path or a level-by-level view, which means BFS and a queue, or does it want every path explored, a full search space, or a solution built up one choice at a time, which means DFS and a stack or recursion? Both run in O(V + E) time on a graph with V nodes and E edges, so speed almost never breaks the tie between them. What breaks the tie is the shape of what the question is actually asking, and that shape is usually visible in the wording before you write a single line of code. This piece covers the exact cue words that separate the two, working code for both, the complexity math, and the mistakes that make an otherwise correct answer look shaky under interview pressure.

What Is the Real Difference Between BFS and DFS?

The real difference between BFS and DFS is the order they visit nodes in, and that order comes directly from the data structure each one is built on. Breadth-first search uses a queue, first in, first out, so it finishes visiting every neighbor at the current distance before it moves one step further out. Depth-first search uses a stack, last in, first out, or the recursion call stack, which does the same job implicitly, so it commits to one path and follows it as deep as it goes before backtracking to try another branch.

Picture a graph as a set of rings expanding outward from a starting node, ring 0 is the start, ring 1 is everything one edge away, ring 2 is everything two edges away, and so on. BFS clears ring 1 completely, then ring 2, then ring 3, always moving outward one full layer at a time. DFS ignores the rings entirely and instead picks one neighbor, then one of that neighbor's neighbors, then one of theirs, diving straight toward the edge of the graph before it ever doubles back to try a sibling branch. Neither order is faster in the worst case, both still touch every reachable node and edge exactly once, but the order changes what each algorithm is naturally good at answering.

How Do You Tell Which One a Problem Wants?

You can tell which one a problem wants from a handful of phrases that show up in the prompt itself, and reading for those phrases before you start coding turns graph problems from a guessing game into pattern matching. The two lists below cover the signals worth watching for, and once you've seen a dozen of these problems the phrasing starts to feel almost like a label on the question.

BFS cue words point at distance, layers, or the shortest route between two points:

  • "shortest path" or "minimum number of steps" between two nodes
  • "fewest" anything: fewest hops, fewest moves, fewest transformations
  • "level order" or "nearest" k nodes from a starting point
  • rotting or spreading simulations, where something propagates outward one step at a time

DFS cue words point at exhaustive search, structure, or a path that doesn't need to be the shortest one:

  • "find all paths" or "does a path exist" without any mention of shortest
  • counting connected components or islands in a grid
  • detecting a cycle, or checking whether a graph is bipartite
  • topological ordering, or any problem that reduces to trying choices and undoing them, which is exactly what backtracking does with the same underlying search

The one word that should make you stop and reread the prompt is "shortest" next to a graph that has different edge weights instead of uniform ones. Neither plain BFS nor plain DFS handles weighted shortest paths correctly, that's Dijkstra's algorithm or Bellman-Ford, and naming that distinction out loud is worth more in an interview than quietly writing a BFS that gives the wrong answer on a weighted graph.

How Do You Implement BFS and DFS in Python?

You implement BFS and DFS in Python with two different container types and one shared habit: tracking which nodes you've already visited so the traversal terminates instead of looping forever on a cycle. The two implementations below assume an adjacency list, a dictionary mapping each node to a list of its neighbors, which is how most interview problems hand you the graph or how you'll build it yourself from a grid or an edge list.

BFS Implementation

BFS needs a queue, and Python's collections.deque gives you O(1) additions and removals from both ends, which a plain list does not for removals from the front.

from collections import deque

def bfs(graph, start):
    visited = {start}
    queue = deque([start])
    order = []

    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)

    return order

Adding a node to visited the moment it's enqueued, not when it's dequeued, is the detail that keeps this correct. Wait until the dequeue step to mark it visited and the same node can end up in the queue multiple times through different paths, which wastes work and can distort results on problems that count steps or layers.

DFS Implementation

DFS has two equally valid forms, recursive and iterative, and interviewers are usually happy with either as long as you can explain the tradeoff between them.

def dfs_recursive(graph, node, visited=None, order=None):
    if visited is None:
        visited, order = set(), []
    visited.add(node)
    order.append(node)
    for neighbor in graph[node]:
        if neighbor not in visited:
            dfs_recursive(graph, neighbor, visited, order)
    return order


def dfs_iterative(graph, start):
    visited = set()
    stack = [start]
    order = []

    while stack:
        node = stack.pop()
        if node in visited:
            continue
        visited.add(node)
        order.append(node)
        for neighbor in graph[node]:
            if neighbor not in visited:
                stack.append(neighbor)

    return order

Notice the iterative version marks a node visited when it's popped, not when it's pushed, the opposite of the BFS version above. That's not a typo, it's because a node can land on the stack more than once before it's ever processed, and checking on pop is what keeps that safe. The recursive version avoids that problem entirely since each call only ever processes one node, but it trades that simplicity for a recursion depth equal to how far the search goes, which matters on the deep graphs covered below.

What Is the Time and Space Complexity of BFS vs DFS?

Both BFS and DFS run in O(V + E) time on a graph with V nodes and E edges, since a correct traversal visits every node once and looks at every edge once while deciding where to go next. That shared bound is exactly why complexity rarely decides which one to use, the real difference shows up in space instead.

| Metric | BFS | DFS | | --- | --- | --- | | Time | O(V + E) | O(V + E) | | Space (data structure) | O(V) worst case, the queue can hold an entire layer | O(V) worst case, the stack or recursion depth | | Space (typical case) | Close to worst case on wide, shallow graphs | Close to O(depth) on narrow, deep graphs | | Finds shortest path | Yes, on unweighted graphs with uniform edge cost | No, finds a path, not necessarily the shortest one | | Natural fit | Level order, shortest hops, spreading simulations | Exhaustive search, backtracking, cycle detection |

The practical gap between them shows up on graph shape, not on the formula. A wide graph where one node connects to thousands of others pushes BFS's queue toward that same size, since an entire layer can be sitting in the queue at once. A deep, narrow graph, a long chain with few branches, pushes DFS toward a stack or recursion depth equal to that chain's length instead. Neither is universally lighter, it depends on whether the graph in front of you is wide or deep.

Does BFS Always Find the Shortest Path?

BFS finds the shortest path only when every edge costs the same to cross, which is the unweighted graph case almost every interview version of this problem uses. Because BFS clears one full layer before moving to the next, the first time it reaches a target node is guaranteed to be by the fewest possible edges, there's no shorter route it could have skipped over, since every closer node was already checked first.

That guarantee breaks the moment edges have different weights. A path with three cheap edges can cost less than a path with one expensive edge, and BFS has no concept of cost at all, it only counts hops. Reaching for BFS on a weighted shortest-path problem gives you a path with the fewest edges, which is frequently not the cheapest one. Dijkstra's algorithm, which replaces BFS's plain queue with a priority queue ordered by running cost, is the correct tool once edge weights enter the picture, and saying that out loud the moment you notice weighted edges is a strong signal to an interviewer that you're pattern-matching correctly rather than reaching for the first traversal that comes to mind.

What Mistakes Cost Candidates Points on Graph Traversal Problems?

The single most common mistake on graph traversal problems is forgetting the visited set entirely, or adding a node to it at the wrong moment. Without one, a cyclic graph sends the traversal in circles forever, and even an acyclic graph can revisit the same node through multiple paths, which quietly turns a linear traversal into something exponential. Get the timing wrong instead of skipping it, marking a node visited on dequeue in BFS rather than on enqueue, and the algorithm still terminates but does noticeably more work than it needs to, which shows up as a wrong answer on any problem that's counting steps.

The second mistake is picking BFS out of habit for a problem that actually wants every path, or picking DFS for one that specifically wants the shortest. This happens most often when a candidate recognizes "graph problem" but skips the second read for the specific word that reveals which traversal it wants. Reading the prompt once for the general shape and a second time specifically hunting for words like "shortest", "fewest", "all paths", or "any path" catches this before it costs a full restart mid-interview.

The third is choosing recursive DFS on a graph that could be deep without considering the consequences. Python's default recursion limit is 1,000 frames, and a long chain, a skewed tree, or a large grid traversed diagonally can hit that ceiling and throw a RecursionError in the middle of an otherwise correct solution. The iterative version with an explicit stack sidesteps the limit entirely, so it's worth mentioning as the safer default whenever the input size isn't small enough to rule the risk out.

How Do You Talk Through a Graph Traversal Solution in an Interview?

Talk through a graph traversal solution by naming the cue word you spotted before you write any code, since that one sentence tells the interviewer you're pattern-matching the problem rather than guessing. Something as short as "this asks for the fewest steps between two nodes, so I'm reaching for BFS with a queue" does more work in ten seconds than a correct implementation that never explains why BFS was the right call.

State your visited-set strategy next, specifically when a node gets marked visited relative to when it's added to the queue or stack, because that's the detail most likely to trip up your own code under pressure and the detail an interviewer is listening for to gauge how well you actually understand the traversal rather than having memorized it. If the graph could plausibly be deep, say so and pick the iterative form deliberately instead of defaulting to recursion out of habit.

Complexity deserves the same two-part treatment our breakdown of interview time complexity covers for every other algorithm family: state the shared O(V + E) bound first, then note whether this particular graph is wide or deep, since that's what actually determines the space cost in practice. That second half is usually what separates a senior-sounding answer from a memorized one.

Where to Practice Graph Traversal Next

BFS and DFS cover the two traversal orders, but most interview graph problems are really asking you to combine one of them with a second idea, tracking distance for shortest-path variants, tracking a path for reconstruction, or tracking state for problems like course scheduling that need a topological order built on top of DFS. Our curated question bank pulls graph problems from real onsite reports rather than an unfiltered public archive, so the versions you practice match what loops are actually asking instead of a problem that stopped showing up years ago.

Pair this decision framework with pattern recognition across the rest of the coding round so graph traversal becomes one identifiable shape among several, not an isolated trick you only remember when a problem looks exactly like one you've seen before. If your loop also includes a design round, where graph thinking resurfaces in the form of dependency graphs and service topologies, our 45-minute framework covers the same structured reasoning for that stage.

Frequently Asked Questions

Is DFS the same as backtracking?

No, DFS is the traversal order, and backtracking is a specific way of using that order to search a space of candidate solutions, adding a choice, recursing, then undoing it before trying the next option. Every backtracking solution is a DFS underneath, but plenty of plain DFS problems, counting connected components for instance, never undo anything because there's nothing to backtrack out of. Our backtracking breakdown covers that template in full.

Does BFS always find the shortest path?

Only on unweighted graphs, or graphs where every edge costs the same to cross. BFS counts hops, not cost, so on a graph with different edge weights it can return a path with fewer edges that actually costs more than a path with more, cheaper edges. Dijkstra's algorithm is the correct traversal once weights enter the picture.

Why does DFS sometimes hit a recursion limit?

Recursive DFS adds one stack frame per node on the current path, and Python's default recursion limit is 1,000 frames. A long chain, a skewed tree, or a large grid can exceed that and throw a RecursionError even though the algorithm's logic is correct. The iterative version, which uses an explicit stack instead of the call stack, doesn't have that ceiling.

Can you do DFS with a queue or BFS with a stack?

Swapping the container changes the traversal order entirely, so a "BFS" built on a stack stops being breadth-first the moment you make that swap, it just becomes a different DFS variant. The container isn't an implementation detail you can freely substitute, it's the thing that defines which algorithm you're running.

What is the time complexity of BFS and DFS?

Both run in O(V + E) time on a graph with V nodes and E edges, since a correct traversal visits every node once and checks every edge once. Space differs based on the graph's shape: BFS's queue tends toward the width of the widest layer, while DFS's stack or recursion depth tends toward how deep the graph goes.

When would you use BFS over DFS in a real interview?

Reach for BFS whenever the prompt asks for the shortest path, the fewest steps, or a level-by-level view on an unweighted graph, a rotting oranges or word ladder style problem is the classic shape. Reach for DFS when the problem wants every path explored, a count of connected pieces, a cycle check, or a topological order, none of which care about finding the shortest route.