BFS vs DFS: Difference Between Breadth-First and Depth-First Search

BFS vs DFS: Difference Between Breadth-First and Depth-First Search

A complete, GATE-ready comparison of the two foundational graph traversal algorithms — with pseudocode, worked examples, complexity proofs, and clear decision rules.

Last updated: April 18, 2026  |  Topic: Algorithms  |  Level: B.Tech / GATE CS

Key Takeaways

  • BFS uses a Queue (FIFO); DFS uses a Stack or recursion (LIFO).
  • Both run in O(V + E) time — identical time complexity.
  • BFS guarantees the shortest path (fewest edges) in unweighted graphs; DFS does not.
  • DFS is preferred for topological sort, cycle detection in directed graphs, and backtracking.
  • BFS is preferred for level-order traversal, shortest path (unweighted), and peer discovery.
  • Space complexity differs: BFS uses O(W) for queue width; DFS uses O(H) for recursion height.
  • GATE 2024 and 2025 both tested graph traversal order — expect a numerical in GATE 2026.

Advertisement

1. What Is BFS (Breadth-First Search)?

Breadth-First Search (BFS) is a graph traversal algorithm that visits all vertices at the current depth level before moving to vertices at the next depth level. Think of it as exploring a graph in concentric rings outward from the source vertex.

BFS was first formalized by Konrad Zuse in 1945 and later independently described by Edward Moore (1959) for finding shortest paths through mazes. Today it underlies GPS shortest-route computation, social network friend suggestions, and peer-to-peer broadcasting.

Core BFS Idea: Always visit the nearest unvisited node next.

Achieved by: enqueue source → loop: dequeue → mark visited → enqueue all unvisited neighbours.

2. What Is DFS (Depth-First Search)?

Depth-First Search (DFS) is a graph traversal algorithm that explores as far as possible along each branch before backtracking. It dives deep into one path until it hits a dead end (no unvisited neighbours), then retreats to the last branch point and tries another path.

DFS mimics how you solve a maze: walk straight until stuck, backtrack, try another corridor. It is the backbone of topological sorting, Tarjan’s SCC algorithm, and the recursive structure of many divide-and-conquer problems.

Core DFS Idea: Always visit the deepest unvisited node next.

Achieved by: push source → loop: pop → if unvisited: mark → push all unvisited neighbours.

3. BFS Algorithm — Step-by-Step

Pseudocode

BFS(Graph G, source s):
  visited = set()
  queue  = empty Queue
  result = []

  visited.add(s)
  queue.enqueue(s)

  while queue is not empty:
    u = queue.dequeue()
    result.append(u)
    for each neighbour v of u in G:
      if v not in visited:
        visited.add(v)
        queue.enqueue(v)

  return result

Trace on a Sample Graph

Consider the undirected graph:

    1
   / \
  2   3
 / \   \
4   5   6

Starting BFS from vertex 1 (adjacency order: left before right):

StepDequeueQueue afterVisited
Start[1]{1}
11[2, 3]{1,2,3}
22[3, 4, 5]{1,2,3,4,5}
33[4, 5, 6]{1,2,3,4,5,6}
44[5, 6]unchanged
55[6]unchanged
66[]unchanged

BFS order: 1 → 2 → 3 → 4 → 5 → 6 (level by level)

BFS in Python

from collections import deque

def bfs(graph, start):
    visited = set([start])
    queue   = deque([start])
    order   = []
    while queue:
        node = queue.popleft()
        order.append(node)
        for nb in graph[node]:
            if nb not in visited:
                visited.add(nb)
                queue.append(nb)
    return order

4. DFS Algorithm — Step-by-Step

Pseudocode (Iterative)

DFS(Graph G, source s):
  visited = set()
  stack   = empty Stack
  result  = []

  stack.push(s)

  while stack is not empty:
    u = stack.pop()
    if u not in visited:
      visited.add(u)
      result.append(u)
      for each neighbour v of u in G (reverse order for left-to-right):
        if v not in visited:
          stack.push(v)

  return result

Trace on the Same Graph

DFS from vertex 1 (push neighbours in reverse so left child is processed first):

StepPopStack afterVisited
Start[1]{}
11[3, 2]{1}
22[3, 5, 4]{1,2}
34[3, 5]{1,2,4}
45[3]{1,2,4,5}
53[6]{1,2,4,5,3}
66[]{1,2,4,5,3,6}

DFS order: 1 → 2 → 4 → 5 → 3 → 6 (deep into left branch first)

DFS in Python (Recursive)

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

Advertisement

5. BFS vs DFS: Full Comparison Table

ParameterBFSDFS
Full FormBreadth-First SearchDepth-First Search
Data StructureQueue (FIFO)Stack (LIFO) / Recursion
Traversal OrderLevel by levelBranch as deep as possible, then backtrack
Time ComplexityO(V + E)O(V + E)
Space ComplexityO(V) — stores all nodes at max-width levelO(V) — stores nodes along current path
Shortest Path✅ Guaranteed (unweighted graphs)❌ Not guaranteed
CompletenessComplete (finds solution if it exists)Complete only for finite graphs
OptimalOptimal for uniform-cost edgesNot optimal
Cycle Detection✅ Detects cycles (undirected)✅ Detects back edges (directed & undirected)
Topological Sort❌ Not directly✅ Yes (post-order DFS)
Connected Components✅ Yes✅ Yes
Bipartite Check✅ Easier (2-colouring level by level)✅ Yes
Memory (wide graph)High — queue can hold entire levelLow — stack depth = path length
Memory (deep graph)Low — stops exploring level when doneHigh — stack grows to full depth
ImplementationSlightly more code (explicit queue)Simpler (recursion handles stack)
Tree Edge TypesTree edges only (no back/forward edges in tree BFS)Tree, Back, Forward, Cross edges
ApplicationsGPS, web crawlers, P2P networks, social graphsMaze solving, scheduling, compilers, SCCs

6. Time and Space Complexity Analysis

Time Complexity: O(V + E) for Both

Each vertex is enqueued/pushed exactly once: O(V). Each edge is examined exactly twice (once from each endpoint in undirected graphs): O(E). Total: O(V + E).

T(BFS) = T(DFS) = O(V + E)

For dense graphs (E = V²): O(V²)

For sparse graphs (E ≈ V): O(V)

Space Complexity: Where They Differ

Graph ShapeBFS SpaceDFS Space
Complete binary tree (height h)O(2h) — last level widthO(h) — path to leaf
Linear chain (path graph)O(1) — only 1 node per levelO(V) — full path on stack
Star graph (1 hub, V–1 leaves)O(V) — all leaves in queue at onceO(1) — visits hub then each leaf
Grid graph (√V × √V)O(√V) — BFS front is a diagonalO(V) worst case

Rule of thumb for GATE: BFS is better when the graph is deep but narrow; DFS is better when the graph is wide but shallow.

7. When to Use BFS vs DFS

ProblemUseWhy
Shortest path (unweighted graph)BFSBFS explores by edge count; first arrival = shortest
Web crawler (level-by-level)BFSPoliteness: explore nearby pages before going deep
Social network — degrees of separationBFSBFS distance equals hops from source
Topological sort of a DAGDFSPost-order DFS gives reverse topological order
Detect cycle in a directed graphDFSBack edges (ancestor still on stack) = cycle
Strongly Connected Components (Kosaraju/Tarjan)DFSBoth algorithms build on DFS finish times
Solving puzzles / mazes with backtrackingDFSExplores a full path before backtracking
Bipartite graph checkingBFS2-colour adjacent levels; easier to reason about
Finding all paths between two nodesDFSDFS naturally enumerates paths via backtracking
Level-order tree traversalBFSDefinition of level-order is BFS order

8. GATE-Level Worked Examples

Example 1 — BFS Traversal Order (GATE 2019 pattern)

Graph (undirected):

Vertices: {0,1,2,3,4,5}
Edges: 0-1, 0-2, 1-3, 1-4, 2-5
Adjacency list (sorted): 0:[1,2], 1:[0,3,4], 2:[0,5], 3:[1], 4:[1], 5:[2]

Q: What is the BFS traversal starting from vertex 0?

Solution:

Queue: [0] → dequeue 0, enqueue 1,2 → Queue:[1,2]
Queue: [1,2] → dequeue 1, enqueue 3,4 → Queue:[2,3,4]
Queue: [2,3,4] → dequeue 2, enqueue 5 → Queue:[3,4,5]
dequeue 3 → no new → Queue:[4,5]
dequeue 4 → no new → Queue:[5]
dequeue 5 → no new → Queue:[]

Answer: 0 → 1 → 2 → 3 → 4 → 5

Example 2 — DFS Back Edges (GATE 2022 pattern)

Directed Graph:

Edges: A→B, A→C, B→D, D→A, C→D

Q: Identify all back edges in DFS starting from A (visit in alphabetical order).

Solution:

DFS path: A(1) → B(2) → D(3) → A [already visited, D→A is a back edge!]
         backtrack → C(4) → D [already visited, C→D is a cross edge]

Back edge: D → A (D is a descendant; A is an ancestor still on the stack)

This back edge proves the graph has a cycle: A → B → D → A

Example 3 — Minimum Hops (BFS Shortest Path)

Q: In a social network graph, you are at person A. What is the minimum number of connections to reach person F?

A - B - C - F
|
D - E - F  (another path)

Graph edges: A-B, B-C, C-F, A-D, D-E, E-F

BFS from A:

Level 0: {A}
Level 1: {B, D}
Level 2: {C, E}
Level 3: {F} ← reached!

Answer: 3 hops (minimum). Both paths are length 3.

DFS might find A→B→C→F first (3 hops) but is not guaranteed to find the minimum.

Advertisement

9. Five Common Mistakes Students Make

Mistake 1 — Marking visited AFTER dequeue in BFS

Many students mark a node visited when they dequeue it, not when they enqueue it. This causes the same node to be enqueued multiple times, leading to incorrect order and O(V²) or worse performance. Always mark visited when you enqueue, not when you dequeue.

Mistake 2 — Assuming DFS always finds the shortest path

DFS finds a path, not the shortest path. In an unweighted graph, only BFS guarantees that the first time you reach a node, you’ve taken the fewest edges. Exam questions frequently test this distinction.

Mistake 3 — Confusing back edges with cross edges in DFS

A back edge goes from a node to its ancestor (still on the DFS stack). A cross edge goes to a node in a different DFS subtree (already fully processed). Only back edges indicate cycles in directed graphs. Cross edges do not.

Mistake 4 — Ignoring disconnected components

A single BFS or DFS call from one source only visits the connected component containing that source. To traverse the entire graph, you must restart BFS/DFS from every unvisited vertex. Omitting this is a common source of wrong answers in GATE.

Mistake 5 — Stack overflow in recursive DFS on large graphs

Python has a default recursion limit of 1000. For a path graph of 10,000 nodes, recursive DFS crashes. The fix is either iterative DFS (explicit stack) or increasing the limit with sys.setrecursionlimit(). In GATE theory this doesn’t matter; in coding interviews it does.

10. Frequently Asked Questions

When should you use BFS instead of DFS?

Use BFS when you need the shortest path in an unweighted graph, when the solution is likely near the source (peer-to-peer networks, level-order traversal), or when you need to process nodes level by level. BFS guarantees the shortest path in terms of number of edges.

Why does BFS use a Queue and DFS use a Stack?

BFS must always process the earliest-discovered node next — that’s FIFO, exactly what a Queue provides. DFS must always explore the most recently discovered node next (go deep before backtracking) — that’s LIFO, exactly what a Stack (or the call stack in recursion) provides. The data structure directly encodes the traversal strategy.

What is the time and space complexity of BFS vs DFS?

Time: Both are O(V + E) — each vertex and edge is processed once. Space: Both are O(V) worst-case, but the constant differs: BFS queue can hold an entire wide level; DFS stack holds the current path depth. For balanced trees: BFS space = O(V/2) at the leaf level, DFS space = O(log V) for the height.

Can BFS detect cycles in a graph?

Yes. During BFS of an undirected graph, if a neighbour of the current node is already visited and is NOT the node’s parent in the BFS tree, a cycle exists. For directed graphs, DFS back edges are the cleaner method: a back edge (to an ancestor still on the stack) confirms a cycle. BFS can still detect directed cycles but requires tracking node states (white/gray/black).

11. Next Steps

Advertisement

Leave a Comment