What is Breadth-First Search (BFS)?
Short answer
Breadth-First Search (BFS) is an algorithm for traversing a graph/tree that visits vertices "layer by layer" from the starting vertex, using a queue. It guarantees finding the shortest path by edge count in unweighted graphs, and runs in O(V+E) time and O(V) memory.
Detailed answer
Definition
BFS is an algorithm that starts traversing from a given starting vertex and successively visits all vertices at distance 1 edge, then distance 2 edges, and so on. A queue (FIFO) controls the visit order. This "layered" traversal correctly measures the minimum number of edges from the source to every reachable vertex in an unweighted graph.
Key idea
- Put the starting vertex into the queue, mark it as visited, and set its distance to 0.
- While the queue isn't empty: dequeue a vertex u from the front, "expand" it by going through all of its neighbors v.
- If a neighbor v isn't visited yet: mark it visited when adding it to the queue, record its parent v = u, set dist[v] = dist[u] + 1, and push v onto the back of the queue.
Properties and guarantees
- Layering: vertices are visited in order of non-decreasing shortest distance from the source.
- Shortest paths in unweighted graphs: BFS finds the minimum number of edges from the source to every reachable vertex.
- Works for both directed and undirected graphs (respecting edge direction).
- Suits trees (it gives a level-order traversal).
Complexity
- Time: O(V + E), where V is the number of vertices and E is the number of edges.
- Memory: O(V) for the queue and the dist/visited/parent arrays.
Data structures
- A queue (FIFO), controlling the traversal order by level.
- visited, marking that a vertex has already been enqueued (important to mark it when adding, not when dequeuing).
- dist, the distance in edges from the source to a vertex.
- parent, a vertex's parent in the BFS tree, used to reconstruct the path.
Pseudocode (JavaScript)
function bfs(adj, start) {
const n = adj.length;
const dist = Array(n).fill(Infinity);
const parent = Array(n).fill(-1);
const visited = Array(n).fill(false);
const q = [];
let head = 0; // implementing the queue with an array and a head pointer
q.push(start);
visited[start] = true;
dist[start] = 0;
while (head < q.length) {
const u = q[head++];
for (const v of adj[u]) {
if (!visited[v]) {
visited[v] = true; // mark when adding
parent[v] = u; // remember the BFS tree
dist[v] = dist[u] + 1; // distance by layer
q.push(v);
}
}
}
return { dist, parent, visited };
}Reconstructing the path (via parent)
After BFS, you can reconstruct the shortest path from s to t by walking up parent from t to s and reversing the sequence.
function getPath(parent, s, t) {
const path = [];
for (let v = t; v !== -1; v = parent[v]) path.push(v);
path.reverse();
return path[0] === s ? path : []; // if t is unreachable, return an empty path
}Example: shortest path in an undirected graph
The graph is given as adjacency lists. Let's find the shortest path from 0 to 5.
const adj = [
/*0*/ [1, 2],
/*1*/ [0, 3, 4],
/*2*/ [0, 4],
/*3*/ [1, 5],
/*4*/ [1, 2, 5],
/*5*/ [3, 4]
];
const { dist, parent } = bfs(adj, 0);
console.log('dist to 5:', dist[5]); // 3
console.log('path 0->5:', getPath(parent, 0, 5)); // for example [0,1,3,5] or [0,2,4,5]Example: level-order tree traversal
For trees, BFS corresponds to a level-order traversal: first the root, then all nodes at depth 1, then depth 2, and so on.
class Node {
constructor(val, left = null, right = null) {
this.val = val; this.left = left; this.right = right;
}
}
function levelOrder(root) {
if (!root) return [];
const res = [];
const q = [root];
let head = 0;
while (head < q.length) {
const levelSize = q.length - head;
const level = [];
for (let i = 0; i < levelSize; i++) {
const node = q[head++];
level.push(node.val);
if (node.left) q.push(node.left);
if (node.right) q.push(node.right);
}
res.push(level);
}
return res;
}
// Example tree: 1
// / \
// 2 3
// / \ \
// 4 5 6
const root = new Node(1, new Node(2, new Node(4), new Node(5)), new Node(3, null, new Node(6)));
console.log(levelOrder(root)); // [[1],[2,3],[4,5,6]]BFS on a grid: shortest path with obstacles
Cells with value 0 are walkable, 1 are obstacles. We move in 4 directions. BFS returns the length of the shortest path in steps.
function shortestPathGrid(grid, start, goal) {
const m = grid.length, n = grid[0].length;
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
const dist = Array.from({ length: m }, () => Array(n).fill(Infinity));
const q = [];
let head = 0;
const [sx, sy] = start, [gx, gy] = goal;
if (grid[sx][sy] === 1 || grid[gx][gy] === 1) return -1;
dist[sx][sy] = 0;
q.push([sx, sy]);
while (head < q.length) {
const [x, y] = q[head++];
if (x === gx && y === gy) return dist[x][y];
for (const [dx, dy] of dirs) {
const nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] === 0 && dist[nx][ny] === Infinity) {
dist[nx][ny] = dist[x][y] + 1;
q.push([nx, ny]);
}
}
}
return -1; // no path to the goal
}
const grid = [
[0,0,1,0],
[0,0,0,0],
[1,0,1,0],
[0,0,0,0]
];
console.log(shortestPathGrid(grid, [0,0], [3,3]));Variations and applications
- Shortest paths in unweighted graphs and grids (in steps/edges).
- Determining reachability and distances from the source to all vertices.
- Checking connectivity/counting connected components (running BFS from unvisited vertices).
- Checking bipartiteness: coloring by level (alternating colors).
- Multi-source BFS: starting from a set of vertices at once (distance 0), useful for "wave" propagation problems.
BFS vs DFS
- BFS uses a queue and moves layer by layer; DFS uses a stack/recursion and goes deep.
- BFS finds the shortest path in unweighted graphs; DFS gives no such guarantee.
- Memory: BFS can consume more memory on wide levels; DFS is usually more memory-efficient.
When BFS doesn't fit
- Weighted graphs with varying weights: Dijkstra's algorithm is needed (or 0-1 BFS for weights of 0/1, or A* with a heuristic).
- Very wide graphs/levels, causing high memory consumption.
Common errors
- Marking visited too late (when dequeuing), which can let nodes end up in the queue multiple times.
- Using a stack instead of a queue, which gives DFS instead of BFS.
- Forgetting to initialize dist/parent/visited for each BFS run.
- Handling edge direction incorrectly in directed graphs.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.