AlgoVerse

Breadth-first search (BFS)

Explore the graph layer by layer from the source, finding the fewest edges to every node with a queue.

Breadth-first search explores the graph layer by layer: first the source, then all its neighbours (distance 1), then their neighbours (distance 2), and so on. The secret of this order is a queue: a node discovered earlier is also handled earlier.

Read the full explanation ↓
Input:
V=8 · E=10 · src=SPDF export

An eight-node undirected graph in two rows; BFS from S has three layers.

  1. from collections import deque
  2. INF = float("inf")
  3. def bfs(graph, src):
  4. dist = {v: INF for v in graph}
  5. parent = {v: None for v in graph}
  6. dist[src] = 0
  7. queue = deque([src])
  8. while queue:
  9. u = queue.popleft()
  10. for v, _ in graph[u]:
  11. if dist[v] == INF:
  12. dist[v] = dist[u] + 1
  13. parent[v] = u
  14. queue.append(v)
  15. return dist, parent

Invariant: The nodes in the queue are in order of distance, and the first and last differ by at most 1.

Start

RSTUVWXY
  • Current node (u)
  • Shortest-path tree so far
  • Edge being checked

Queue queue

empty

Local variables

src'S'

Orange = changed in this step

graph dict
  • 'R': [('S', 1), ('V', 1)]
  • 'S': [('R', 1), ('W', 1)]
  • 'T': [('W', 1), ('X', 1), ('U', 1)]
  • 'U': [('T', 1), ('X', 1), ('Y', 1)]
  • 'V': [('R', 1)]
  • 'W': [('S', 1), ('T', 1), ('X', 1)]
  • 'X': [('W', 1), ('T', 1), ('U', 1), ('Y', 1)]
  • 'Y': [('X', 1), ('U', 1)]

Call stack

  1. bfs()line 6
Step 1 of 92
Black = dequeue · Orange = key stepenqueue: 0/8 · dequeue: 0/8 · check: 0/20 · discover: 0/7

    How the work grows with input size

    Average of 5 random graphs per n (fixed seeds). Hollow marker = the input on screen.

    • check
    • discover
    • current input (n=8)
    0204060481216202428n = number of nodescheckdiscover
    Show as a table
    ncheckdiscover
    45.23
    813.87
    1220.811
    162815
    2035.419
    2437.223
    2847.227
    Explanation

    The idea

    Breadth-first search explores the graph layer by layer: first the source, then all its neighbours (distance 1), then their neighbours (distance 2), and so on. The secret of this order is a queue: a node discovered earlier is also handled earlier.

    The steps

    1. Every distance is ∞ and the source's is 0. The source goes into the queue.
    2. Take the node at the front of the queue, u.
    3. For each neighbour v of u: if dist[v] is still ∞, v has just been discovered. Record dist[v] = dist[u] + 1 and parent[v] = u, and add v to the back of the queue.
    4. Repeat from step 2 until the queue is empty.

    Why it finds shortest paths

    Nodes enter the queue in order of distance, so the first time we reach a node we came along a shortest path (the fewest edges). Edge weights do not matter to BFS; for a weighted graph, use Dijkstra.

    Cost

    Every node enters and leaves the queue once, and every edge is checked once (twice in an undirected graph), so the time is O(V + E).

    What to watch in the trace

    • The number next to each node is dist; nodes not discovered yet show ∞.
    • The queue is under the picture: nodes leave on the left and join on the right.
    • The blue edges are the BFS tree (from parent). On the "star" input the queue fills at once; on the "long chain" it always holds one node.
    • Turn on "Predict" and guess which node leaves the queue next.