BFS (Breadth-First) Visualization — Step by Step
Explores a graph level by level using a queue. Finds shortest path in unweighted graphs.
// quick answer
BFS (Breadth-First) is a graph algorithm that runs in O(V+E) time on average with O(V) space. Explores a graph level by level using a queue. Finds shortest path in unweighted graphs.
How BFS (Breadth-First) Works
Breadth-First Search explores all neighbors at the current depth before moving to the next level. It uses a FIFO queue and guarantees finding the shortest path in unweighted graphs. Time complexity is O(V + E) where V is vertices and E is edges.
BFS (Breadth-First) Step by Step
A queue drives everything
BFS explores a graph in expanding rings around the start node, and the entire behaviour comes from one choice of data structure: a FIFO [queue](/workspace/queue). Nodes are enqueued when discovered and dequeued in the same order, so nodes discovered early are processed before nodes discovered late. Combined with a *visited* set that ensures each node is handled once, this forces the traversal to finish all nodes at distance 1 before touching any node at distance 2, and so on. Swap the queue for a stack and the identical loop becomes DFS — the container *is* the algorithm.
A concrete trace
Take a five-node graph with edges 0–1, 0–4, 1–2, 1–3, and 3–4. Start BFS at node 0. Enqueue 0 → queue [0]. Dequeue 0, visit it, enqueue its neighbours 1 and 4 → queue [1, 4]. Dequeue 1, visit, enqueue its undiscovered neighbours 2 and 3 → queue [4, 2, 3]. Dequeue 4, visit — its neighbours 0 and 3 are already discovered, so nothing is added. Dequeue 2, then dequeue 3. Final visit order: 0, 1, 4, 2, 3 — both of 0's direct neighbours before any node two hops away.
Levels are distances
Label each node with the level at which BFS discovers it: node 0 is level 0; nodes 1 and 4 are level 1; nodes 2 and 3 are level 2. That level is exactly the minimum number of edges from the start — and this is BFS's superpower. Notice node 4: the path 0 → 1 → 3 → 4 also reaches it, using three edges, but BFS finds it first via the direct edge 0–4. Because all level-k nodes are processed before any level-(k+1) node, the *first* time BFS reaches a node is provably along a shortest path in edge count.
Mark nodes at discovery, not at visit
One subtlety keeps the queue clean: a node should be marked as discovered when it is enqueued, not later when it is dequeued. In the trace above, node 3 is discovered from node 1; when node 4 is processed moments later and also sees node 3, the check prevents a duplicate enqueue. Without it, popular nodes would enter the queue once per incoming edge, inflating memory and work. The visualizer shows this as the three node states — unvisited, in queue (frontier), and visited — with each enqueue and dequeue as its own step.
Why it costs O(V + E)
Every vertex enters and leaves the queue at most once — that is O(V) queue operations. Every edge is examined at most once from each endpoint when its owner is dequeued — O(E) edge inspections. Total: O(V + E), linear in the size of the graph, with O(V) extra space for the queue and visited set in the worst case. This is optimal for traversal: any algorithm that claims to explore a graph must at least look at every vertex and edge once. The cost of BFS is essentially the cost of reading the graph.
Time & Space Complexity
| Best Case | Average Case | Worst Case | Space |
|---|---|---|---|
| O(V+E) | O(V+E) | O(V+E) | O(V) |
BFS (Breadth-First) Pseudocode
function BFS(graph, start):
queue = [start]
visited = {start}
while queue not empty:
node = dequeue()
process node
for neighbor in adj[node]:
if neighbor not visited:
visited.add(neighbor)
enqueue(neighbor)Press play in the visualizer above to watch each line execute with live highlighting.
BFS (Breadth-First)Code in JavaScript, Python & Java
JavaScript
function bfs(graph, start) {
// graph: Map or object mapping node -> array of neighbours
const visited = new Set([start]);
const queue = [start];
const order = [];
while (queue.length > 0) {
const node = queue.shift(); // dequeue (FIFO)
order.push(node);
for (const neighbor of graph[node]) {
if (!visited.has(neighbor)) {
visited.add(neighbor); // mark at discovery time
queue.push(neighbor);
}
}
}
return order; // nodes in breadth-first visit order
}Python
from collections import deque
def bfs(graph, start):
"""graph: dict mapping node -> list of neighbours."""
visited = {start}
queue = deque([start])
order = []
while queue:
node = queue.popleft() # dequeue (FIFO)
order.append(node)
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor) # mark at discovery time
queue.append(neighbor)
return order # nodes in breadth-first visit orderJava
import java.util.*;
public class BFS {
// graph: adjacency list, graph.get(u) = neighbours of u
public static List<Integer> bfs(List<List<Integer>> graph, int start) {
boolean[] visited = new boolean[graph.size()];
Deque<Integer> queue = new ArrayDeque<>();
List<Integer> order = new ArrayList<>();
visited[start] = true;
queue.add(start);
while (!queue.isEmpty()) {
int node = queue.poll(); // dequeue (FIFO)
order.add(node);
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true; // mark at discovery time
queue.add(neighbor);
}
}
}
return order;
}
}When to Use BFS (Breadth-First)
- Use it for shortest paths in unweighted graphs — fewest hops between two people in a social network, fewest moves in a puzzle, fewest clicks between web pages. BFS guarantees minimality; DFS does not.
- Use it for level-order processing: anything that must be handled ring by ring, such as multi-source flood fill or computing distances from a set of starting points.
- Use it to test reachability and connected components — BFS from a node visits exactly the nodes in its component.
- Use it as the skeleton of other algorithms: web crawlers, garbage-collector marking phases, and bipartiteness checks are all BFS with extra bookkeeping.
- Do not use it when edges have different weights — a two-hop path of weight 1+1 beats a one-hop path of weight 10, and BFS cannot see that. Use Dijkstra's algorithm instead.
- Be careful on very wide graphs: the queue holds an entire frontier at once, which can be huge (a node with a million neighbours puts all of them in memory). DFS is often lighter when paths are needed but shortest ones are not.
BFS (Breadth-First) — Frequently Asked Questions
Does BFS find the shortest path?
Yes — but only in unweighted graphs, where "shortest" means fewest edges. Because BFS processes all nodes at distance k before any node at distance k+1, the first time it reaches a node is guaranteed to be along a minimum-hop path. If edges carry different weights, BFS can return a badly suboptimal route; weighted shortest paths need Dijkstra or a similar algorithm.
What is the difference between BFS and DFS?
The container. BFS uses a first-in-first-out queue and explores level by level, radiating outward from the start; DFS uses a last-in-first-out stack and dives down one branch as far as possible before backtracking. Both run in O(V + E) and visit the same set of reachable nodes — they differ in visit order, memory profile, and which problems they naturally solve.
Why does BFS use a queue?
The FIFO discipline is what creates the level-by-level order. Nodes discovered earlier — which are always closer to the start — get processed before nodes discovered later. That single property produces the expanding-frontier behaviour and the shortest-path guarantee. Replace the queue with a stack and the same code performs depth-first search instead, losing both properties.
What is the time and space complexity of BFS?
Time is O(V + E): each vertex is enqueued and dequeued at most once, and each edge is examined a constant number of times. Space is O(V) in the worst case, since the visited set holds every reachable vertex and the queue can hold an entire frontier — which on wide, bushy graphs may be a large fraction of all nodes at once.
Can BFS handle disconnected graphs?
A single BFS visits only the nodes reachable from its start, so on a disconnected graph it covers one connected component and stops. To traverse everything, wrap it in a loop: for every node not yet visited, launch a new BFS from it. Each launch discovers exactly one component, which is also the standard way to count or label connected components.
Compare BFS (Breadth-First)
- BFS (Breadth-First) vs DFS (Depth-First) — when to choose which, head to head
- BFS (Breadth-First) vs Dijkstra's Algorithm — when to choose which, head to head
Related Algorithms
- DFS (Depth-First) Visualization — Explores a graph by going as deep as possible along each branch before backtracking, using a stack.
- Dijkstra's Algorithm Visualization — Finds the shortest path from a source to all other nodes in a weighted graph with non-negative edges.
References & Further Reading
- Wikipedia — Breadth-first search
- Cormen, Leiserson, Rivest & Stein — Introduction to Algorithms (4th ed., MIT Press)