What is Breadth-First Search?
Breadth-first search (BFS) is a graph traversal that visits every node reachable from a start node in order of distance: first the start, then all of its neighbours, then their unvisited neighbours, and so on. It uses a FIFO queue and runs in O(V + E) time, and in an unweighted graph the first time BFS reaches a node is always along a path with the fewest possible edges.
How it works
- Mark the start node
sas seen, setdist[s] = 0and putsin a queue. - Dequeue the node
uat the front of the queue. - For each neighbour
vofuthat has not been seen: mark it seen, setdist[v] = dist[u] + 1andparent[v] = u, and enqueue it. - Skip neighbours that were already seen — they are waiting in the queue or already done.
- Repeat until the queue is empty. Nodes that were never seen are unreachable from
s.
Why a queue gives shortest paths
Think of ripples on a pond. The queue only ever holds nodes from two consecutive levels: those at distance d at the front, and the nodes at distance d + 1 they are discovering at the back. Because the queue is first-in, first-out, every node at distance d leaves before any node at distance d + 1.
So when BFS discovers v from u, no shorter route to v can exist — it would have to pass through a node that was dequeued earlier, and that node would have discovered v first. This is why dist[v] is final the moment it is set, and why following parent pointers back from any node spells out a fewest-edge path.
The argument relies on every edge costing the same. With weights, a route with more edges can be cheaper, and the FIFO queue no longer hands out nodes in order of cost. Dijkstra's algorithm fixes exactly that by replacing the queue with a priority queue.
Worked example
The visualizer opens on a 7-node undirected graph with edges A–B, A–C, B–C, B–D, B–E, C–D, C–F, D–E, D–F, E–G and F–G (weights hidden — BFS ignores them). Neighbours are examined in alphabetical order. Starting from A:
- Dequeue A: discover B and C at distance 1. Queue: B, C.
- Dequeue B: A and C are already seen; discover D and E at distance 2. Queue: C, D, E.
- Dequeue C: A, B and D are seen; discover F at distance 2. Queue: D, E, F.
- Dequeue D: every neighbour is already seen. Dequeue E: discover G at distance 3.
- Dequeue F and G: nothing new. The queue is empty, so BFS stops.
The visit order is A, B, C, D, E, F, G, and the levels are {A}, {B, C}, {D, E, F}, {G}. The tree edges A–B, A–C, B–D, B–E, C–F and E–G form the BFS tree, which gives a fewest-edge route to every node — for example A → B → E → G for G.
BFS vs DFS
Both visit every node and edge once, so both are O(V + E). They differ in the frontier they keep and in what they are good for:
- Order: BFS visits by distance from the start; DFS follows one path as deep as it can, then backtracks.
- Memory: BFS holds a whole level at once — on a star-shaped graph that is V − 1 nodes. DFS holds only the current path, O(depth).
- Answers: BFS gives shortest paths in unweighted graphs and levels; DFS gives discovery and finish times, which power cycle detection, topological sort and strongly connected components.
On the demo graph, DFS from A dives down the single chain A → B → C → D → E → G → F, six edges deep, while the BFS tree is only three levels deep.
Common mistakes
- Marking nodes as visited when they are dequeued instead of enqueued. A node can then enter the queue once per incoming edge, wasting up to O(E) queue space and risking an overwritten
dist. Mark it the moment you enqueue it. - Using a list as the queue. In Python
list.pop(0)shifts every element, costing O(n) per dequeue; usecollections.deque. - Using BFS for weighted shortest paths. It returns the route with the fewest edges, not the lowest total weight.
- Forgetting disconnected graphs. One BFS only reaches the start's component. Loop over all nodes and start a new BFS from each unseen one — the number of starts is the number of connected components.
- Scanning an adjacency matrix. Finding neighbours by scanning a row makes BFS O(V²) instead of O(V + E).
Where BFS is used
- Grids and mazes: each cell is a node and each move an edge, so BFS finds the minimum number of moves (knight moves, robot paths, word ladder).
- Multi-source BFS: enqueue several starts at distance 0 to get the distance to the nearest source, as in "rotting oranges" or nearest-hospital maps.
- Bipartite check: colour nodes by level parity; an edge between two nodes of the same colour reveals an odd cycle.
- 0-1 BFS: with weights of only 0 and 1, a double-ended queue (push 0-edges to the front) replaces Dijkstra.
- Level-order traversal of a tree is BFS on a tree.
Outside interviews, BFS powers web crawlers, "people within two hops" features on social networks, broadcast in computer networks, and Cheney's copying garbage collector, which copies live objects in breadth-first order.
Time and space complexity
| Case | Time | Space | Why |
|---|---|---|---|
| Adjacency list | O(V + E) | O(V) | Every node is enqueued and dequeued once; every adjacency list is scanned once (twice per undirected edge). |
| Adjacency matrix | O(V²) | O(V) | Listing a node's neighbours means scanning its whole row of V cells. |
| Grid of R × C cells | O(R · C) | O(R · C) | Each cell has at most 4 neighbours, so E = O(V). |
Breadth-First Search in Python
from collections import deque
def bfs(graph, s):
"""graph: {node: [neighbours]}. Returns dist[v] = fewest edges from s to v."""
dist = {s: 0}
seen = {s}
queue = deque([s])
while queue: # while queue not empty
u = queue.popleft() # u <- queue.dequeue()
for v in graph[u]: # each neighbour v of u
if v not in seen:
dist[v] = dist[u] + 1
seen.add(v)
queue.append(v)
# else: v was already seen - skip it
return distThe visualizer above shows the same code in Python, C++, Java and JavaScript.
Frequently asked questions
Does BFS find the shortest path?
Yes, in an unweighted graph (or one where every edge has the same weight): the first time BFS reaches a node is along a fewest-edge path. With different weights use Dijkstra's algorithm, or 0-1 BFS when weights are only 0 and 1.
What data structure does BFS use?
A first-in, first-out queue, plus a seen set or distance array. Replacing the queue with a stack turns the traversal into a depth-first order.
What is the time complexity of BFS?
O(V + E) with an adjacency list, because every node is dequeued once and every edge examined once (twice if undirected). With an adjacency matrix it is O(V²). The queue and seen set need O(V) extra space.
Why mark a node as visited when it is enqueued, not dequeued?
Marking on enqueue guarantees each node enters the queue exactly once, so the queue never exceeds V entries and dist is set once, on a shortest route. Marking on dequeue lets the same node be queued once per incoming edge.
How do I count connected components with BFS?
Loop over every node; whenever you meet one that has not been seen, start a new BFS from it and add one to the count. The total work is still O(V + E).
Keep learning
- DFS Visualizer — Depth-First SearchDepth-first search explores a graph by going as deep as possible, then backtracking. See DFS step by step with discovery and finish times, back edges and code.
- Dijkstra's Algorithm VisualizerDijkstra's algorithm finds the shortest paths from one node in a graph with non-negative weights. Watch every relaxation step by step, with heap-based code.
- Topological Sort VisualizerA topological sort orders the nodes of a DAG so every edge points forward. Watch Kahn's algorithm step by step, see why cycles block it, and get the code.
- Queue VisualizerInteractive queue visualizer: watch enqueue and dequeue move the front and rear of an array queue, and see the wasted-space problem that circular queues fix.
- Tree Traversal Visualizer — Inorder, Preorder, Postorder, Level OrderVisualize inorder, preorder, postorder and level-order tree traversal on one tree: the visit order, the call stack or queue, the code, and when to use each.
- Graph Algorithm VisualizerInteractive graph algorithm visualizer: draw or generate a graph and step through BFS, DFS, Dijkstra, Bellman-Ford, Kruskal, Prim, topological sort and SCCs.
Last updated