BFS (Breadth-First) Visualization — Step by Step

Explores a graph level by level using a queue. Finds shortest path in unweighted graphs.

GraphintermediateUpdated

// 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 CaseAverage CaseWorst CaseSpace
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 order

Java

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)

Related Algorithms

References & Further Reading