Skip to content

Breadth-first search

Exploring from a start vertex with a queue reaches vertices in order of their distance in edges, and the parents lead back along a shortest route.

Helps to know first
This topic

First, the picture

A small maze, explored from its top-left cell. Watch the search spread one layer at a time, with its queue below.

Spreading out one layer at a time

A breadth-first search through a small maze, with its queue below

void bfs(int s){    int ring[V];    int head = 0, count = 0;    for (int v = 0; v < V; v++) {        dist[v] = -1;        parent[v] = -1;    }    dist[s] = 0;    ring[(head + count) % V] = s;    count++;    while (count > 0) {        int u = ring[head];        head = (head + 1) % V;        count--;        for (const struct enode *e = adj[u]; e != NULL; e = e->next) {            if (dist[e->to] < 0) {                dist[e->to] = dist[u] + 1;                parent[e->to] = u;                ring[(head + count) % V] = e->to;                count++;            }        }    }} int main(void){    const char *maze[ROWS] = { "...#.", ".#...", ".###.", "....." };    build(maze);    bfs(START);    for (int r = 0; r < ROWS; r++) {        for (int c = 0; c < COLS; c++) {            if (maze[r][c] == '#')                printf(" #");            else                printf(" %d", dist[r * COLS + c]);        }        printf("\n");    }    int path[V];    int len = 0;    for (int v = GOAL; v != -1; v = parent[v]) {        path[len] = v;        len++;    }    printf("route:");    for (int i = len - 1; i >= 0; i--)        printf(" %d", path[i]);    printf("\n");    for (int v = 0; v < V; v++) {

A 4 × 5 maze: # is a wall. Cell r · 5 + c is a vertex; 15 cells are open.

reached
0 of 15
in the queue
0
cell 0

Output

0.00 / 22.75 s
Describe this picture

The picture has two parts, one above the other. On top is the maze: 4 rows of 5 square cells, numbered 0 to 19 row by row from the top left, with 5 dark wall cells and 15 open ones. Under it is a strip of 20 boxes, ring, the search’s queue, with a “head” marker above it and a “tail” marker below; the boxes between them are lit, the others greyed. Under the picture are the lines of maze.c the clip runs, with the line about to run marked, one sentence on the current step, two readouts (the cells reached and the cells waiting in the queue), the control, and the output. The clip plays once, about 23 seconds. First every cell is marked unreached. Then cell 0 gets the number 0 and enters the ring. Again and again the cell at the head is taken out and ringed in the maze, the head marker moves one box right, and each open neighbour not yet reached gets a number one larger, turns to the frontier tone, and lands in the box at the tail. The numbers spread outwards in rings: 1 on cells 1 and 5, 2 on cells 2 and 10, and so on to 7 on cell 19, the far corner. Taken cells change to the visited tone. When the queue is empty, the route to cell 4, the goal at the top right, is read back along the parents, a cell at a time, as a line through the cells 4, 9, 8, 7, 2, 1 and 0. The clip rests on the output, the maze’s distances and route: 0 1 2 7 8 9 4. The control, “Start cell”, is a slider over the 15 open cells: a new start replays the search from there. The transport plays and pauses, steps one beat back or forward, and scrubs; a paused step is kept in the page address as bfs.step, and the start as bfs.start.

A wave from the start

Breadth-first search explores a graph from a start vertex in layers: first the start, then every vertex one edge away, then every vertex two edges away, and so on. Here the graph is a maze. Each open cell is a vertex, numbered r · 5 + c, and two open cells side by side or one above the other are joined by an edge. The maze’s lists are built as in storing a graph, an arc each way for each pair of open neighbours.

The search keeps a queue, first in, first out, as in ring buffers. It takes the vertex at the front, and puts each of its neighbours that has not been reached yet at the back.

The code and its cost

The queue and the marks

bfs keeps three arrays of 20: dist, a vertex’s distance in edges or −1 while it is unreached; parent, the vertex it was reached from; and ring, the queue.

dist[s] = 0;
ring[(head + count) % V] = s;
count++;
while (count > 0) {
    int u = ring[head];
    head = (head + 1) % V;
    count--;
    for (const struct enode *e = adj[u]; e != NULL; e = e->next) {
        if (dist[e->to] < 0) {
            dist[e->to] = dist[u] + 1;
            parent[e->to] = u;
            ring[(head + count) % V] = e->to;
            count++;
        }
    }
}

A vertex’s distance is set when it is queued, not when it leaves the queue. That is what stops a second neighbour from queueing it again: by then its dist is no longer −1. So each vertex enters the queue at most once, and a ring of V cells never fills.

Layers

The queue holds at most two layers at once: the end of layer k, then the start of layer k + 1 behind it. So the vertices leave in order of distance, every vertex of layer k before any of layer k + 1. From cell 0 the layers are:

DistanceCells
00
11, 5
22, 10
37, 15
48, 16
59, 17
64, 14, 18
719

The first time a vertex is reached, it is reached by a shortest route in edges, and nothing later improves its number. BFS does not try every route: it reaches each vertex once.

The tree of parents

Each reached vertex but the start has one parent, so the parents form a tree. Following them from any vertex leads back to the start by a shortest route. main reads the route to the goal, cell 4, backwards into path, then prints it from the start:

 0 1 2 # 6
 1 # 3 4 5
 2 # # # 6
 3 4 5 6 7
route: 0 1 2 7 8 9 4

The goal has one open neighbour, cell 9, so its route is the only shortest one.

The cost

Each vertex is queued once and taken once, and each list is walked once, when its vertex is taken. The lists of an undirected graph hold 2E nodes, one for each end of each edge, so that is V + 2E steps in all: O(V + E), for a graph stored as lists.

When it is not enough

BFS finds the route with the fewest edges. When edges have weights, such as road lengths, the fewest edges is not always the least total, and Dijkstra’s algorithm is needed.

Running it yourself

gcc -std=c11 -Wall -Wextra -pedantic maze.c -o maze
./maze
gcc -std=c11 -Wall -Wextra -pedantic -DSTART=19 maze.c -o maze
./maze

Worked example

The house graph. On the graph of storing a graph, from 0, visiting neighbours smallest first: the distances of vertices 0 to 5 are 0 1 1 2 2 3, the parents of 1 to 5 are 0 0 1 2 4, and the route to 5 is 0 2 4 5.

Another start. From cell 19, the goal is only 3 steps away, 19, 14, 9, 4, though 19 is the farthest cell from 0.

A large grid. On an open 1,000 × 1,000 grid, BFS from a corner reaches all 1,000,000 cells, each once, and the far corner at distance 1,998: 999 steps across and 999 down.

Two checks. Which cells are at distance 6 from 0? (4, 14 and 18.) Why is dist set when a vertex is queued, not when it leaves? (So that no later neighbour queues it again.)

Where it’s used

Routes with equal steps. Moving through a grid or a network where each step costs the same.

Puzzles. Each position of a puzzle is a vertex and each move an edge; BFS finds a solution in the fewest moves.

Social networks. Everyone within two links of a person is the first two layers from them.

Flood fill. A paint program’s fill spreads from the clicked pixel to every touching pixel of the same colour.

Reference card

Code or ideaMeaning
dist[v] = -1unreached
take from the head, queue at the tailthe queue, first in, first out
dist[e->to] = dist[u] + 1 when queuedeach vertex enters the queue once
layersall of distance k before any of k + 1
parent[v]the tree of shortest routes back to the start
V + E stepsthe cost with adjacency lists

Next, depth-first search goes deep before it goes wide.

Platform: C11, gcc on x86-64 Linux: int 4 bytes, pointers 8, little-endian. Addresses in the pictures are short stand-ins, by one rule for the whole course; sizes are the real ones.

Keep reading with a free account.

Graphs is a members’ section. An account costs nothing.

By creating an account you accept the terms of use.

Sign in

Already have an account? Use the way you signed up.

Are you 13 or over?

Accounts and email updates are for people aged 13 or over. We keep only your answer, never your date of birth.

End of the lesson

Where to go next.

New lessons by email

Are you 13 or over?

Accounts and email updates are for people aged 13 or over. We keep only your answer, never your date of birth.

Phasorium

Account

Sign inWith Google, GitHub or your email

Subjects

Site

Look