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.
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.
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.
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 resultTrace on a Sample Graph
Consider the undirected graph:
1 / \ 2 3 / \ \ 4 5 6
Starting BFS from vertex 1 (adjacency order: left before right):
| Step | Dequeue | Queue after | Visited |
|---|---|---|---|
| Start | — | [1] | {1} |
| 1 | 1 | [2, 3] | {1,2,3} |
| 2 | 2 | [3, 4, 5] | {1,2,3,4,5} |
| 3 | 3 | [4, 5, 6] | {1,2,3,4,5,6} |
| 4 | 4 | [5, 6] | unchanged |
| 5 | 5 | [6] | unchanged |
| 6 | 6 | [] | 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 order4. 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 resultTrace on the Same Graph
DFS from vertex 1 (push neighbours in reverse so left child is processed first):
| Step | Pop | Stack after | Visited |
|---|---|---|---|
| Start | — | [1] | {} |
| 1 | 1 | [3, 2] | {1} |
| 2 | 2 | [3, 5, 4] | {1,2} |
| 3 | 4 | [3, 5] | {1,2,4} |
| 4 | 5 | [3] | {1,2,4,5} |
| 5 | 3 | [6] | {1,2,4,5,3} |
| 6 | 6 | [] | {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 order5. BFS vs DFS: Full Comparison Table
| Parameter | BFS | DFS |
|---|---|---|
| Full Form | Breadth-First Search | Depth-First Search |
| Data Structure | Queue (FIFO) | Stack (LIFO) / Recursion |
| Traversal Order | Level by level | Branch as deep as possible, then backtrack |
| Time Complexity | O(V + E) | O(V + E) |
| Space Complexity | O(V) — stores all nodes at max-width level | O(V) — stores nodes along current path |
| Shortest Path | ✅ Guaranteed (unweighted graphs) | ❌ Not guaranteed |
| Completeness | Complete (finds solution if it exists) | Complete only for finite graphs |
| Optimal | Optimal for uniform-cost edges | Not 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 level | Low — stack depth = path length |
| Memory (deep graph) | Low — stops exploring level when done | High — stack grows to full depth |
| Implementation | Slightly more code (explicit queue) | Simpler (recursion handles stack) |
| Tree Edge Types | Tree edges only (no back/forward edges in tree BFS) | Tree, Back, Forward, Cross edges |
| Applications | GPS, web crawlers, P2P networks, social graphs | Maze 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).
For dense graphs (E = V²): O(V²)
For sparse graphs (E ≈ V): O(V)
Space Complexity: Where They Differ
| Graph Shape | BFS Space | DFS Space |
|---|---|---|
| Complete binary tree (height h) | O(2h) — last level width | O(h) — path to leaf |
| Linear chain (path graph) | O(1) — only 1 node per level | O(V) — full path on stack |
| Star graph (1 hub, V–1 leaves) | O(V) — all leaves in queue at once | O(1) — visits hub then each leaf |
| Grid graph (√V × √V) | O(√V) — BFS front is a diagonal | O(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
| Problem | Use | Why |
|---|---|---|
| Shortest path (unweighted graph) | BFS | BFS explores by edge count; first arrival = shortest |
| Web crawler (level-by-level) | BFS | Politeness: explore nearby pages before going deep |
| Social network — degrees of separation | BFS | BFS distance equals hops from source |
| Topological sort of a DAG | DFS | Post-order DFS gives reverse topological order |
| Detect cycle in a directed graph | DFS | Back edges (ancestor still on stack) = cycle |
| Strongly Connected Components (Kosaraju/Tarjan) | DFS | Both algorithms build on DFS finish times |
| Solving puzzles / mazes with backtracking | DFS | Explores a full path before backtracking |
| Bipartite graph checking | BFS | 2-colour adjacent levels; easier to reason about |
| Finding all paths between two nodes | DFS | DFS naturally enumerates paths via backtracking |
| Level-order tree traversal | BFS | Definition of level-order is BFS order |
8. GATE-Level Worked Examples
Example 1 — BFS Traversal Order (GATE 2019 pattern)
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)
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)
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.
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).