BFS vs DFS? The Question That Fixed My Graph Solutions

BFS vs DFS? The Question That Fixed My Graph Solutions

posted Originally published at www.codeintuition.io 3 min read

For a long time I treated BFS and DFS as interchangeable. Two ways to visit every node, pick whichever came to mind first. Then an interview handed me a grid that wanted the minimum number of steps, and a recursive DFS felt faster to type, so that's what went down. The path that came back was valid and far too long. No error. Just an answer that passed the small cases and fell apart on the one that mattered, with no obvious reason why.

What finally clicked:

  • The two traversals hold different invariants. They don't just visit in a different order.
  • The queue gives BFS its shortest path guarantee. The stack makes DFS commit to one branch.
  • "Minimum" in an unweighted graph turns out to be a reliable signal for BFS.
  • "Detect", "order", and "all paths" point to DFS.
  • Once edges carry weights, neither applies, and Dijkstra's algorithm takes over.

Where my approach was breaking

The mistake wasn't picking the slower traversal. It was reaching for whichever traversal got practiced most recently instead of reading what the problem actually asked for.

A grid that wants the fewest steps is asking for a distance guarantee, and DFS doesn't offer one. It happily commits to a branch and walks 30 cells deep before it would ever consider the 8 cell route, then stops the moment it touches the goal, satisfied with a path that happens to be valid. The wrong choice there has nothing to do with DFS being worse. It comes from never asking what kind of answer the problem needed.

The one question that changed it

Before writing any code now, the question is always the same: does this problem need a distance guarantee or a structural property? If the answer is distance, that's BFS. If it's structure, meaning cycles, ordering, or which nodes connect to which, that's DFS.

It sounds almost too simple, and that's the point. The decision was never about which algorithm you can implement faster. It's about which guarantee the problem is quietly demanding.

What that looked like on the same six node graph

The clearest way to feel the difference is to run both on one small graph and watch the order. Take six nodes with edges 0→1, 0→2, 1→3, 1→4, 2→4, 2→5, starting from node 0.

from collections import deque

graph = {0: [1, 2], 1: [3, 4], 2: [4, 5], 3: [], 4: [], 5: []}

def bfs(start):
    order, seen, q = [], {start}, deque([start])
    while q:
        node = q.popleft()
        order.append(node)
        for nxt in graph[node]:
            if nxt not in seen:
                seen.add(nxt)
                q.append(nxt)
    return order

def dfs(start):
    order, seen, stack = [], set(), [start]
    while stack:
        node = stack.pop()
        if node in seen:
            continue
        seen.add(node)
        order.append(node)
        for nxt in reversed(graph[node]):
            if nxt not in seen:
                stack.append(nxt)
    return order

print(bfs(0))  # [0, 1, 2, 3, 4, 5]
print(dfs(0))  # [0, 1, 3, 4, 2, 5]

BFS visits in exact distance order: everything one hop from the source (1, 2) before anything two hops away (3, 4, 5). DFS visits 3 before it ever gets back to 2, even though 3 sits further from the source. It cares about depth, not distance. Swapping the queue for a stack is the only structural change between the two functions, and it produces the entire difference in behaviour.

Why this works (the part that took digging)

A queue is first in, first out, so the nodes you discover at distance d always come out behind the remaining d-1 nodes and ahead of the d+1 ones. That ordering is the shortest path guarantee, with no explicit distance tracking needed. A stack is last in, first out, so the most recent branch always gets expanded next, which is depth first by construction.

There's a memory split that follows from the same fact. BFS holds the full frontier, so its peak memory tracks the widest level of the graph. DFS holds only the current path, so its memory tracks the deepest one. Tracing both algorithms frame by frame in the graph course, watching the queue and stack contents change at every step, is what makes the distance ordering stop feeling abstract.

What to do when you're still guessing

If you're staring at a graph problem and reaching for habit, run it through this instead:

If the problem asks for Reach for
Shortest path or fewest moves, equal cost edges BFS
Level by level processing BFS
Nearest target BFS
Cycle detection DFS
Topological order DFS
Connected components DFS
All paths or backtracking DFS
Shortest path with weighted edges Neither, use Dijkstra

I originally posted this on my own blog, a slightly longer version with a directed cycle detection walkthrough.

What's a graph problem that finally made the BFS versus DFS choice click for you, the one where guessing wrong actually cost you in an interview?

More Posts

The Senior Angular Take‑Home That Made Me Rethink Tech Interviews

Karol Modelskiverified - Apr 2

FAANG Coding Interviews Aren’t the Same: The DSA Patterns Each Company Actually Tests

prakhar_srv - May 12

How to Know If You’re Ready for FAANG Interviews

prakhar_srv - May 9

Arrays vs Linked Lists: The Real Difference Most Interview Prep Misses

prakhar_srv - May 10

Backtracking Finally Clicked When I Fixed One Habit

prakhar_srv - May 21
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

13 comments
4 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!