BFS explores a graph level by level: all neighbours at distance 1 before any at distance 2
Uses a FIFO queue (deque) — O(1) enqueue and dequeue
First time you reach a node is the shortest hop-count path in unweighted graphs
Time: O(V+E), Space: O(V) for queue and visited set
Mark nodes visited when enqueuing — prevents duplicate work and O(V²) queue blow-up
Biggest mistake: using list.pop(0) as a queue — O(n) per dequeue kills performance
✦ Definition~90s read
What is BFS?
BFS (Breadth-First Search) is a graph traversal algorithm that explores nodes level by level, using a queue to track the frontier. It guarantees the shortest path in unweighted graphs because it visits nodes in order of their distance from the source.
★
Imagine throwing a stone into a pond.
The core mechanism is simple: dequeue a node, process it, and enqueue all unvisited neighbors. This level-order behavior makes BFS ideal for problems like shortest path in a grid, connected components, bipartite checking, and level-order tree traversal.
However, BFS has a critical memory footprint: the queue can grow to O(V) in the worst case (e.g., a star graph where the root connects to all other nodes), and the visited set must be checked at enqueue time, not at dequeue time. The classic pitfall—visited set timing—occurs when you mark a node as visited only when you pop it from the queue.
This allows duplicate enqueues, causing the queue to explode exponentially (e.g., a complete graph with 1000 nodes could enqueue each node thousands of times, blowing memory). The fix is to mark visited immediately upon enqueue, which caps queue size at O(V).
In practice, BFS is the right tool when you need shortest path in unweighted graphs or level-order traversal, but avoid it for weighted graphs (use Dijkstra) or when memory is tight and the graph is dense (consider DFS or iterative deepening). Real-world examples: BFS powers social network 'degrees of separation' (LinkedIn, Facebook), web crawlers (Googlebot), and network broadcast protocols.
The queue memory explosion is a silent killer in production—I've seen it crash services processing 10M+ node graphs because someone forgot to mark visited on enqueue.
Plain-English First
Imagine throwing a stone into a pond. Ripples spread outward one ring at a time. BFS works the same way: it visits every point that's one ripple away from the starting point, then every point two ripples away, and so on. It's the natural way to find the shortest path in places where every step costs the same — like navigating a subway map.
How BFS Actually Explores — And Where It Breaks
Breadth-First Search (BFS) is a graph traversal algorithm that explores nodes level by level, using a queue to guarantee that all nodes at distance k are visited before any node at distance k+1. The core mechanic: dequeue a node, process it, then enqueue all its unvisited neighbors. This gives BFS the property of finding the shortest path in unweighted graphs — every edge traversed adds exactly one to the distance.
In practice, BFS runs in O(V + E) time and O(V) space for the queue and visited set. The queue can grow to the width of the graph — in a balanced binary tree that's O(n/2), but in a star graph or dense layer it's O(V). That's where memory explosion hits: if you mark visited late (after processing instead of before enqueueing), the same node gets enqueued multiple times, blowing the queue to O(V²) in worst case.
Use BFS when you need shortest path in unweighted graphs, level-order traversal, or any problem where distance from source matters. Real systems use BFS for social network friend recommendations (2–3 hops), web crawler frontier management, and network broadcast routing. The visited-set timing is not a theoretical quirk — it's the difference between a crawler that finishes and one that OOMs on a 10k-node graph.
Mark Visited Before Enqueueing
If you mark visited after dequeuing, the same node enters the queue multiple times — exponential growth in dense graphs, not just a constant factor.
Production Insight
Social graph friend-of-friend crawler: marking visited on dequeue caused queue to hold 4M duplicate entries for a 200k-user subgraph, exhausting heap.
Symptom: GC thrashing followed by OutOfMemoryError on a 4GB heap, with queue size oscillating between 2M and 5M.
Rule: always mark visited at enqueue time — the queue must never hold a node that's already been discovered.
Key Takeaway
BFS guarantees shortest path in unweighted graphs only if you process nodes in FIFO order — no shortcuts.
Visited set timing is the #1 BFS bug: mark before enqueue, not after dequeue, or face exponential queue growth.
Space complexity is O(width) — know your graph's branching factor before you run BFS in production.
thecodeforge.io
BFS Queue Memory Explosion — Visited Set Timing Pitfall
Bfs Breadth First Search
How BFS Works — Plain English and Step-by-Step
BFS answers the question: 'What are all the nodes I can reach, in order of how many hops they are from my starting point?' Think of dropping a stone in a pond — ripples spread outward one ring at a time, and BFS visits nodes the same way: every node at distance 1 first, then every node at distance 2, and so on.
The algorithm requires three ingredients: a queue (FIFO) to decide the order of visiting, a visited set to avoid revisiting nodes, and the graph itself.
Step-by-step: 1. Add the start node to the queue and mark it visited. 2. While the queue is non-empty, dequeue the front node. 3. Process the dequeued node (record it, check a goal, etc.). 4. For each neighbour of the current node: if unvisited, mark it visited and enqueue it. 5. Repeat from step 2.
Worked example on the graph A-B, A-C, B-D, B-E, C-F starting from A: Round 1: dequeue A. Enqueue B, C. Visited={A,B,C}. Order=[A] Round 2: dequeue B. Enqueue D, E (A already visited). Visited={A,B,C,D,E}. Order=[A,B] Round 3: dequeue C. Enqueue F (A visited). Visited={A,B,C,D,E,F}. Order=[A,B,C] Round 4: dequeue D. No new neighbours. Order=[A,B,C,D] Round 5: dequeue E. F already visited. Order=[A,B,C,D,E] Round 6: dequeue F. Done. Order=[A,B,C,D,E,F]
Notice B and C (distance 1 from A) both appear before D, E, F (distance 2). That level-by-level guarantee is why BFS finds shortest paths in unweighted graphs: the first time you reach a node, you have used the fewest possible hops.
Production Insight
If you skip the visited set, even a tiny cycle explodes the queue to O(V^2) memory.
Production graphs often contain hidden cycles from legacy data — always guard.
Rule: mark visited when enqueuing, not when dequeuing.
Key Takeaway
Queue (FIFO) + visited set = core BFS.
Mark visited on enqueue to prevent duplicates.
First visit = shortest path in unweighted graphs.
BFS on a Graph
The implementation uses Python's collections.deque, which provides O(1) append and popleft. Never use a plain list as a BFS queue — list.pop(0) is O(n) and will make your BFS O(V*E) instead of O(V+E). The visited set uses hashing for O(1) membership checks, which is essential when the graph is large.
ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Package: io.thecodeforge.python.algorithmsfrom collections import deque
defbfs(graph, start):
"""Visit all reachable nodes in BFS order."""
visited = set()
queue = deque([start])
visited.add(start)
order = []
while queue:
node = queue.popleft() # FIFO — dequeue from left
order.append(node)
for neighbour in graph[node]:
if neighbour notin visited:
visited.add(neighbour)
queue.append(neighbour) # enqueue to rightreturn order
# Adjacency list representation
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
print(bfs(graph, 'A')) # ['A', 'B', 'C', 'D', 'E', 'F']
Output
['A', 'B', 'C', 'D', 'E', 'F']
Production Insight
Using list.pop(0) instead of deque is the #1 performance mistake in BFS code.
For graphs with 100K+ nodes, O(n) pop turns BFS from seconds to hours.
Rule: always import deque for queues — it's a one-line fix that prevents O(V²) runtime.
Key Takeaway
deque.popleft() is O(1); list.pop(0) is O(n).
If BFS is slow, check your queue implementation first.
Visited set: O(1) check using hash set – don't use list membership.
Shortest Path with BFS
BFS naturally finds the shortest path in an unweighted graph. Track the parent of each node to reconstruct the path.
Storing full paths in the queue instead of parent pointers works but costs memory — each path copy is O(length).
For large graphs with long paths, use a parent dictionary (node -> previous node) and reconstruct after reaching target.
That cuts memory from O(path length × queue size) to O(V).
Key Takeaway
BFS guarantees shortest hop count when edges have uniform weight.
Use parent pointers for memory efficiency in large graphs.
Return None explicitly if target is unreachable — don't assume connectivity.
Level-Order Tree Traversal
Level-order traversal is BFS applied to a binary tree. To separate the output by level, snapshot the queue length before processing each level. That snapshot tells you exactly how many nodes belong to the current level — process exactly that many, collecting children for the next level as you go. This avoids the need for sentinel values or separate level-tracking variables.
ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Package: io.thecodeforge.python.algorithmsfrom collections import deque
classTreeNode:
def__init__(self, val, left=None, right=None):
self.val, self.left, self.right = val, left, right
deflevel_order(root):
ifnot root:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue) # nodes at this level
level = []
for _ inrange(level_size):
node = queue.popleft()
level.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(level)
return result
# Build tree: 1# / \# 2 3# / \ \# 4 5 6
root = TreeNode(1,
TreeNode(2, TreeNode(4), TreeNode(5)),
TreeNode(3, None, TreeNode(6)))
print(level_order(root)) # [[1], [2, 3], [4, 5, 6]]
Output
[[1], [2, 3], [4, 5, 6]]
Production Insight
Snapshoting level_size before the inner loop is simple and efficient — no extra data structures.
But if you modify the queue during processing (e.g., adding nodes from different levels), the snapshot becomes invalid.
In production, ensure the inner loop only adds children of the current level, not from elsewhere.
Key Takeaway
level_size = len(queue) before inner loop gives you the exact number of nodes at current depth.
Process exactly level_size nodes — children go to the next iteration.
This pattern works for any BFS where you need to separate levels.
Beyond shortest paths, BFS is the go-to for two classic problems:
Connected Components — In an undirected graph, run BFS from each unvisited node. Every BFS run discovers one connected component. Count the runs. This is used in social network analysis (friend groups), image segmentation, and network reliability.
Bipartite Check — A graph is bipartite if you can color nodes with two colors such that no edge connects same-colored nodes. BFS works perfectly: assign start node color 0, then assign neighbors color 1, then their neighbors color 0, and so on. If you ever see an edge connecting same-colored nodes, the graph is not bipartite. This is used in scheduling problems (e.g., assigning tasks to two machines without conflicts) and in recommendation systems.
ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# Package: io.thecodeforge.python.algorithmsfrom collections import deque
defconnected_components(graph):
visited = set()
components = []
for node in graph:
if node notin visited:
component = []
queue = deque([node])
visited.add(node)
while queue:
n = queue.popleft()
component.append(n)
for nei in graph[n]:
if nei notin visited:
visited.add(nei)
queue.append(nei)
components.append(component)
return components
defis_bipartite(graph):
color = {}
for node in graph:
if node notin color:
queue = deque([node])
color[node] = 0while queue:
n = queue.popleft()
for nei in graph[n]:
if nei notin color:
color[nei] = 1 - color[n]
queue.append(nei)
elif color[nei] == color[n]:
returnFalsereturnTrue
graph = {
0: [1, 3],
1: [0, 2],
2: [1, 3],
3: [0, 2]
}
print(connected_components(graph)) # [[0,1,2,3]] one componentprint(is_bipartite(graph)) # True
graph2 = {
0: [1, 2, 3],
1: [0, 2],
2: [0, 1, 3],
3: [0, 2]
}
print(is_bipartite(graph2)) # False (triangle with extra edge)
Output
[[0, 1, 2, 3]]
True
False
Production Insight
Connected components via BFS is simple but watch out for memory if the graph is huge — visited set grows to O(V).
For bipartite check, an odd-length cycle immediately breaks the check. Real-world graphs often have millions of nodes; BFS is fine if the graph fits in memory, but for streaming graphs you need incremental algorithms.
Rule: always initialize colors for all nodes before starting BFS to avoid KeyError.
Key Takeaway
BFS from each unvisited node counts connected components.
Bipartite check = BFS with alternating colors.
Odd cycle = not bipartite.
BFS on a Grid — Shortest Path in 2D Matrices
BFS is ideal for grid-based shortest path problems (e.g., shortest path in a maze). The grid is a graph where each cell is a node, and edges exist to adjacent cells (up, down, left, right). Use BFS to find the minimum number of steps from start to target.
Key trick: represent each cell's neighbors by delta arrays (dx, dy). Use a visited 2D array (same size as grid) or a set of tuples. The queue stores (row, col, steps) or better yet, store (row, col) and track steps in a separate distance array.
This pattern appears in countless interview problems and real applications like robot path planning, game map navigation, and network routing in mesh grids.
ExamplePYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# Package: io.thecodeforge.python.algorithmsfrom collections import deque
defshortest_path_grid(grid, start, target):
"""grid: list of lists, 0 = open, 1 = blocked."""
rows, cols = len(grid), len(grid[0])
sr, sc = start
tr, tc = target
if grid[sr][sc] == 1or grid[tr][tc] == 1:
return -1
visited = [[False]*cols for _ inrange(rows)]
queue = deque()
queue.append((sr, sc))
visited[sr][sc] = True
steps = 0
dirs = [(-1,0), (1,0), (0,-1), (0,1)] # up, down, left, rightwhile queue:
for _ in range(len(queue)): # process level by level to count steps
r, c = queue.popleft()
if (r, c) == target:
return steps
for dr, dc in dirs:
nr, nc = r+dr, c+dc
if0 <= nr < rows and0 <= nc < cols andnot visited[nr][nc] and grid[nr][nc] == 0:
visited[nr][nc] = True
queue.append((nr, nc))
steps += 1
return -1# unreachable# Example
grid = [
[0, 0, 0, 0],
[1, 1, 0, 1],
[0, 0, 0, 0],
[0, 1, 1, 0]
]
print(shortest_path_grid(grid, (0,0), (3,3))) # steps = 6 (correct)
Output
6
Production Insight
Visited 2D array is O(R*C) memory — fine for small grids but not for sparse or massive maps.
For huge grids, consider a hash set of visited (r,c) tuples, trading some speed for memory.
Level-by-level processing (inner for loop) gives you exact step count without storing distance per cell.
Rule: Always check boundaries and blocked cells before accessing visited array.
Key Takeaway
Grid BFS = standard BFS with direction deltas.
Process level-by-level to count steps directly.
Visited array or set prevents infinite loops in open spaces.
Why BFS on a Disconnected Graph Will Fail Your Pipeline
You ran BFS on a graph that looked fine in tests. In production, it silently missed half the nodes. Classic. BFS from a single source only explores the connected component containing that source. If your graph is disconnected — and most real-world graphs are — you get incomplete results. This is not a bug in BFS; it is a bug in your assumptions. Social networks, computer networks, and dependency graphs are rarely fully connected. The fix is to wrap your BFS in a loop over all vertices, checking each one for visitation before starting a new traversal. This guarantees you hit every component. It also gives you a free side effect: you can count the number of connected components in the same pass. A simple tweak that prevents a silent data corruption that will cost you a Saturday.
BfsDisconnected.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// io.thecodeforgeimport java.util.*;
publicclassBfsDisconnected {
publicstaticList<Integer> bfsAllComponents(List<List<Integer>> graph) {
int n = graph.size();
boolean[] visited = newboolean[n];
List<Integer> result = newArrayList<>();
Queue<Integer> queue = newLinkedList<>();
for (int i = 0; i < n; i++) {
if (!visited[i]) {
queue.add(i);
visited[i] = true;
while (!queue.isEmpty()) {
int node = queue.poll();
result.add(node);
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.add(neighbor);
}
}
}
}
}
return result;
}
}
Your QA dataset is a single component. Production is a forest. If you skip the outer loop, BFS silently returns a subset of your data. No error. No warning. Just wrong answers downstream.
Key Takeaway
On any graph that might be disconnected, always iterate BFS over all vertices. Count components for free. Never trust a single-source traversal in production.
Why Your BFS Is O(N²) When It Should Be O(N+M)
The standard complexity claim is O(V+E) for BFS. But if you are representing your graph as an adjacency matrix, congratulations: you just turned a linear algorithm into a quadratic one. Every time BFS checks neighbors of a node, an adjacency matrix forces you to scan all V columns. That is O(V) per node, giving you O(V²). For a sparse graph with millions of nodes and only a few edges each, this is catastrophic. The adjacency list representation — an array of lists or vectors — lets BFS iterate only over existing edges. That is how you get the real O(V+E) bound. Memory is the same story: an adjacency matrix is O(V²) memory, an adjacency list is O(V+E). For a social graph with 100 million users and 200 friends each, the matrix is 10 petabyte garbage. The list is 20 gigabytes. Choose your data structure like your career depends on it. It does.
BfsComplexity.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// io.thecodeforgeimport java.util.*;
publicclassBfsComplexity {
// Adjacency list: O(V+E) time, O(V+E) memorypublicstaticList<Integer> bfsAdjList(List<List<Integer>> graph, int start) {
int n = graph.size();
boolean[] visited = newboolean[n];
Queue<Integer> queue = newLinkedList<>();
List<Integer> order = newArrayList<>();
visited[start] = true;
queue.add(start);
while (!queue.isEmpty()) {
int node = queue.poll();
order.add(node);
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.add(neighbor);
}
}
}
return order;
}
}
Output
Time: O(V+E) | Space: O(V)
Adj Matrix equivalent: O(V²) time, O(V²) memory
Production Trap:
Do not blame BFS when your adjacency matrix kills performance. That is your fault. For any graph with edges < V²/2, adjacency list is faster and uses less memory. Period.
Key Takeaway
Adjacency list for BFS or you are paying quadratic tax. If you see O(V²) in your BFS, check your data structure first. It is never BFS — it is your representation.
● Production incidentPOST-MORTEMseverity: high
Production Outage Caused by Unbounded BFS Queue
Symptom
JVM OutOfMemoryError after minutes of high CPU. Heap dumps showed a 200GB queue of identical nodes.
Assumption
The graph had no cycles, so visited set was considered optional for performance.
Root cause
A misconfiguration introduced a self-referencing edge. Without a visited set, the same node was enqueued repeatedly, growing the queue exponentially until memory ran out.
Fix
Added a visited set and marked nodes when enqueuing. Also added a max-queue-size safeguard to alert before memory exhaustion.
Key lesson
Always use a visited set — even in DAGs, a bug can introduce cycles.
Mark nodes visited at enqueue time, not dequeue time.
Bound queue size to a safe maximum and alert on overflow.
Production debug guideSymptom → Action: What to check when BFS isn't working as expected4 entries
Symptom · 01
Queue grows uncontrollably / memory spike
→
Fix
Check if visited set is used and updated on enqueue, not dequeue. Look for cycles in graph data.
Symptom · 02
BFS returns infinite loop / never terminates
→
Fix
Verify visited set membership: ensure you add nodes before enqueuing, not after. Print visited size vs nodes processed.
Symptom · 03
Shortest path result is longer than expected
→
Fix
Confirm the graph is unweighted. BFS on weighted graphs gives wrong paths. Check if edges have weights hidden in data.
Symptom · 04
BFS visits nodes out of expected level order
→
Fix
Check if FIFO discipline is intact. If using a list with pop(0), the queue order may be correct but performance tanked. Use deque.
★ BFS Debugging Cheat SheetQuick commands and checks when BFS behaves unexpectedly.
Queue size grows unchecked−
Immediate action
Stop the run and examine graph for cycles or self-loops
Commands
visited.add(node) before queue.append(node)
print(len(queue), len(visited)) per iteration
Fix now
Add visited guard and limit queue to max expected size (e.g., number of nodes)
Shortest path wrong+
Immediate action
Verify graph is unweighted
Commands
for edge in graph[node]: check if edge is tuple with weight
run Dijkstra on same graph and compare distances
Fix now
Switch to Dijkstra if edges have weights, or strip weights before BFS
BFS not exploring all reachable nodes+
Immediate action
Check if visited set is too aggressive (marking as visited before processing)
Commands
print('Visited set size:', len(visited), 'Queue size:', len(queue))
compare against expected node count
Fix now
Ensure visited set starts empty and only marks on enqueue
BFS vs DFS — When to Use Which
Property
BFS
DFS
Data structure
Queue (FIFO)
Stack (LIFO) or recursion
Space complexity
O(V) in worst case (queue holds level)
O(V) in worst case (call stack depth)
Shortest path (unweighted)
Yes — first visit guarantees shortest hop count
No — path may be long, needs full exploration
Cycle detection
Possible but not efficient
Natural with recursion stack
Topological sort
Can use Kahn's algorithm (BFS variant)
Natural with post-order DFS traversal
Connected components
BFS from each unvisited node
DFS from each unvisited node
Bipartite check
Yes — BFS with two-coloring
Yes — DFS with two-coloring
Memory usage dense graph
Queue can hold many nodes (level width)
Stack depth typical smaller unless deep recursion
Memory usage sparse graph
Similar
Similar
When to use
Shortest path, level-order, closer nodes first
Path existence, all paths, topological sort, maze solving with backtracking
Key takeaways
1
BFS uses a queue (deque)
FIFO ensures level-by-level traversal.
2
Mark nodes as visited when enqueuing, not when dequeuing
prevents duplicate processing.
3
BFS finds the shortest path in unweighted graphs because it processes nodes in order of distance.
4
Time
O(V + E); Space: O(V) for the visited set and queue.
5
For level-order traversal, snapshot queue size before the inner loop to group nodes by level.
6
BFS on a grid
use direction deltas and level-by-level step counting.
Common mistakes to avoid
4 patterns
×
Marking nodes visited when dequeuing instead of enqueuing
Symptom
Same node appears in the queue multiple times, leading to O(V²) queue size and incorrect shortest path lengths.
Fix
Mark node as visited at the same moment you enqueue it — before it enters the queue.
×
Using list.pop(0) as a BFS queue
Symptom
BFS becomes O(V*E) instead of O(V+E). For large graphs, performance degrades drastically.
Fix
Always use collections.deque for O(1) popleft operations.
×
Forgetting to handle disconnected graphs
Symptom
BFS only processes nodes reachable from the start; unreachable nodes remain unvisited, giving incomplete traversal.
Fix
If you need all nodes, loop over all nodes and run BFS from each unvisited node (connected components pattern).
×
Not bounding queue size in production
Symptom
A bug or malicious input can cause infinite queue growth, leading to memory exhaustion and crash.
Fix
Add a max queue size check (e.g., if len(queue) > MAX_NODES: raise/alert). Alternatively, limit visited set size.
Why is BFS preferred over DFS for finding shortest paths in unweighted g...
Q04SENIOR
How would you detect if a graph is bipartite using BFS?
Q05SENIOR
Can BFS be used on weighted graphs for shortest path?
Q01 of 05JUNIOR
What data structure does BFS use and why?
ANSWER
BFS uses a FIFO queue (e.g., Python's deque) because it processes nodes in the order they were discovered — first in, first out. This ensures level-by-level traversal. A stack (LIFO) would give DFS behavior.
Q02 of 05JUNIOR
What is the time and space complexity of BFS?
ANSWER
Time: O(V + E) because each node is enqueued once and each edge is examined once (when its source node is dequeued). Space: O(V) for the queue (holds up to the width of the graph) and O(V) for the visited set. In the worst case (star graph), the queue holds V-1 nodes simultaneously.
Q03 of 05SENIOR
Why is BFS preferred over DFS for finding shortest paths in unweighted graphs?
ANSWER
BFS explores nodes in order of their distance from the start. The first time it reaches a node, that path has the minimum number of edges (hops). DFS goes deep first, so the first path found may be long; you would need to explore all paths to guarantee the shortest.
Q04 of 05SENIOR
How would you detect if a graph is bipartite using BFS?
ANSWER
Assign start node a color (e.g., 0). Then BFS: for each neighbor of the current node, if unvisited, assign opposite color (1 - current) and enqueue; if visited and same color as current, graph is not bipartite. This works because BFS detects odd cycles which break bipartiteness.
Q05 of 05SENIOR
Can BFS be used on weighted graphs for shortest path?
ANSWER
No — BFS assumes each edge has equal weight (1). In weighted graphs, the number of hops does not correspond to total weight. For shortest path in weighted graphs, use Dijkstra's algorithm (non-negative weights) or Bellman-Ford (negative weights).
01
What data structure does BFS use and why?
JUNIOR
02
What is the time and space complexity of BFS?
JUNIOR
03
Why is BFS preferred over DFS for finding shortest paths in unweighted graphs?
SENIOR
04
How would you detect if a graph is bipartite using BFS?
SENIOR
05
Can BFS be used on weighted graphs for shortest path?
SENIOR
FAQ · 6 QUESTIONS
Frequently Asked Questions
01
When should I use BFS vs DFS?
Use BFS for shortest path in unweighted graphs, level-order traversal, and finding closest matches. Use DFS for detecting cycles, topological sort, path existence (not shortest), and exploring all possibilities (combinatorics). BFS uses more memory (all nodes at current level) while DFS uses less (just the current path).
Was this helpful?
02
Why must I mark nodes as visited when enqueuing, not when dequeuing?
If you mark when dequeuing, the same node can be added to the queue multiple times before it is processed. This causes O(V²) queue size in dense graphs and incorrect shortest path computations. Mark when enqueuing to ensure each node enters the queue exactly once.
Was this helpful?
03
Does BFS work on disconnected graphs?
A single BFS call only visits nodes reachable from the start node. For a disconnected graph, loop over all nodes and call BFS on any that have not been visited — the same pattern used to count connected components.
Was this helpful?
04
What is the space complexity of BFS in the worst case?
O(V). In the worst case (a star graph), the queue holds all V-1 neighbours simultaneously after the first dequeue. The visited set also grows to O(V).
Was this helpful?
05
Can BFS be implemented recursively?
Technically yes, but it's unnatural — you'd need to pass the queue as a parameter. Iterative BFS is standard and more efficient because recursion adds call stack overhead.
Was this helpful?
06
How do I find the shortest path in a grid using BFS?
Treat each cell as a node with edges to adjacent cells. Use a queue of (row, col) and a visited boolean grid. Process level by level: increment steps after each level. When you reach the target, return steps. This gives the minimum number of moves (assuming each move costs 1).