EXPLORING GRAPHS

Breadth First Search (BFS)

Start here Breadth first search explores a graph layer by layer: first everything 1 step away, then everything 2 steps away, and so on — like ripples spreading out when you drop a stone in water. A bonus: BFS automatically finds the shortest path (fewest edges) to every reachable vertex.

The idea

Start at a source vertex.

  1. First visit all vertices one step away.
  2. Then all vertices two steps away.
  3. ... and so on.

Every visited vertex must be explored once: look at its neighbours and visit the ones not yet visited. Never explore a vertex twice.

So we keep track of two things:

  • Which vertices have been visited (a visited dictionary: True / False).
  • Which visited vertices are still waiting to be explored — kept in a queue.

Everyday example: News spreads in a class. You tell your friends (1 step). They tell their friends (2 steps). Nobody tells the same news twice to someone who already knows it.

The queue — first in, first out

A queue is like a line at a ticket counter: whoever joins first is served first.

class Queue:
    def __init__(self):
        self.queue = []
    def addq(self, v):              # join at the back
        self.queue.append(v)
    def delq(self):                 # leave from the front
        v = None
        if not self.isempty():
            v = self.queue[0]
            self.queue = self.queue[1:]
        return v
    def isempty(self):
        return self.queue == []
    def __str__(self):
        return str(self.queue)
q = Queue()
for i in range(3):
    q.addq(i)
    print(q)            # [0]   then [0, 1]   then [0, 1, 2]
print(q.isempty())      # False
for j in range(3):
    print(q.delq(), q)  # 0 [1, 2]   then 1 [2]   then 2 []
print(q.isempty())      # True

(Note: self.queue[1:] copies the list, so this simple delq is O(n). Python's collections.deque has a truly O(1) popleft(). For learning, the simple class is fine.)

The BFS algorithm

  1. Set visited[v] = False for every vertex. Start with an empty queue.
  2. Visit the start vertex: mark it True and add it to the queue.
  3. While the queue is not empty:

- Remove the vertex j at the front of the queue and explore it: - For each neighbour k of j: if k is not visited, mark it visited and add it to the queue.

  1. Stop when the queue is empty.
def BFSList(AList, v):
    visited = {}
    for i in AList.keys():
        visited[i] = False
    q = Queue()

    visited[v] = True
    q.addq(v)

    while (not q.isempty()):
        j = q.delq()
        for k in AList[j]:
            if (not visited[k]):
                visited[k] = True
                q.addq(k)

    return visited

Full trace — BFS from vertex 7 (undirected airline graph)

Adjacency list: 0:[1, 2, 4], 1:[0, 2], 2:[0, 1], 3:[4, 5, 6], 4:[0, 3, 7], 5:[3, 6, 7, 8], 6:[3, 5], 7:[4, 5, 8], 8:[5, 7, 9], 9:[8].

StepExploreNeighboursNewly visitedQueue after
start——7[7]
174, 5, 84, 5, 8[4, 5, 8]
240, 3, 70, 3[5, 8, 0, 3]
353, 6, 7, 86[8, 0, 3, 6]
485, 7, 99[0, 3, 6, 9]
501, 2, 41, 2[3, 6, 9, 1, 2]
634, 5, 6—[6, 9, 1, 2]
763, 5—[9, 1, 2]
898—[1, 2]
910, 2—[2]
1020, 1—[] → stop

All 10 vertices are visited → every city is reachable from 7.

BFS from vertex 7 drawn as rings: 7 at the centre (level 0), then 4, 5, 8 on the first ring (level 1), then 0, 3, 6, 9 on the second ring (level 2), then 1 and 2 on the third ring (level 3), with the BFS tree edges highlighted
BFS from 7 spreads in layers: level 1 = {4, 5, 8}, level 2 = {0, 3, 6, 9}, level 3 = {1, 2}.

How long does BFS take?

Let n = number of vertices and m = number of edges. Each reachable vertex is visited once (added to the queue) and explored once (removed from the queue). Exploring a vertex means checking all its edges.

RepresentationExploring vertex i costsTotal
Adjacency matrixScan the whole row: n stepsn vertices × n = O(n²)
Adjacency listdegree(i) stepsn (setup) + sum of all degrees = n + 2m → O(n + m)

Why is the sum of degrees 2m? In an undirected graph each edge (i, j) is counted twice: once in degree(i) and once in degree(j). (Check: the undirected airline graph has 13 edges; its degrees are 3, 2, 2, 3, 3, 4, 2, 3, 3, 1, which add up to 26 = 2 × 13 ✔.)

This "add up the cost over the whole run instead of per step" reasoning is another example of amortised analysis.

Why we keep n and m separate: a connected graph can have anywhere from n − 1 edges to n(n − 1)/2 edges. If m is much smaller than n², the adjacency list is much faster. O(n + m) — "linear time" — is the best possible for graphs, because you must look at every vertex and edge at least once.

Recording paths — the parent

BFS tells us whether k is reachable. To get the actual route, remember who discovered k:

When exploring j marks k as visited, set parent[k] = j.

Then from any vertex, follow parent links back to the start.

def BFSListPath(AList, v):
    (visited, parent) = ({}, {})
    for i in AList.keys():
        visited[i] = False
        parent[i] = -1
    q = Queue()

    visited[v] = True
    q.addq(v)

    while (not q.isempty()):
        j = q.delq()
        for k in AList[j]:
            if (not visited[k]):
                visited[k] = True
                parent[k] = j
                q.addq(k)

    return (visited, parent)

Recording distance — the level

Even better: instead of True/False, store each vertex's level (number of edges from the start). Use −1 for "not visited yet".

def BFSListPathLevel(AList, v):
    (level, parent) = ({}, {})
    for i in AList.keys():
        level[i] = -1
        parent[i] = -1
    q = Queue()

    level[v] = 0
    q.addq(v)

    while (not q.isempty()):
        j = q.delq()
        for k in AList[j]:
            if (level[k] == -1):
                level[k] = level[j] + 1
                parent[k] = j
                q.addq(k)

    return (level, parent)

Result of BFS from 7:

Vertex0123456789
level2332112012
parent4004775−178

Shortest path from 7 to 2: start at 2 and follow parents: 2 → parent 0 → parent 4 → parent 7. Reverse it: 7 – 4 – 0 – 2 (3 edges, matches level[2] = 3).

level[k] is the length of the shortest path from the start to k, counted in number of edges.

(Later in the course we'll see weighted graphs, where edges have costs like distance or ticket price; there "shortest" means least total cost, and BFS alone is not enough.)

In one line: BFS explores level by level using a queue, takes O(n + m) with adjacency lists (O(n²) with a matrix), and its parent/level records give shortest paths in number of edges.

Exam words: BFS, visited, explored, queue (FIFO), addq, delq, level, parent, shortest path (number of edges), O(n²) matrix, O(n + m) list, sum of degrees = 2m, amortised.

Quick check

1. In BFS, which data structure holds the visited-but-not-yet-explored vertices?A queue (first in, first out).
2. Run BFS from 9 on the undirected airline graph. Which vertices are at level 1 and level 2?Level 1: {8}. Level 2: {5, 7} (neighbours of 8). Level 3: {3, 6, 4}.
3. Using the parent table above (BFS from 7), what is the shortest path from 7 to 1?1 → 0 → 4 → 7, reversed: 7 – 4 – 0 – 1.
4. A graph has n = 10⁴ vertices and m = 2 × 10⁴ edges. Compare BFS with a matrix vs a list.Matrix: n² = 10⁸ steps. List: n + m = 3 × 10⁴ steps — over 3000 times less work.
5. In the directed airline graph, how many flights (edges) does the shortest route from Madurai (9) to Delhi (0) take?BFS from 9 gives level[0] = 5: 9 → 8 → 5 → 3 → 4 → 0.
INTERACTIVE LEARNING

Try Interactive Playground

See how breadth-first search explores a graph one layer at a time.

Prototype

Start in Manual mode. Read each card, make your prediction, then choose Show next step when you are ready.

Manual mode — you decide every step
Step 1 of 20 · Set up the search

Put A in the queue and mark it visited.

Why? The queue records which vertices still need to be explored.

BFS moves from left to right: it takes the first vertex waiting in the queue.
ABCDEF
  1. enqueue(start); mark visited
  2. current = dequeue()
  3. visit current
  4. enqueue each unvisited neighbour
  5. skip an already visited neighbour
  6. stop when queue is empty

Before you continue

Look at the highlighted item. What should the algorithm do next?

Complexity

Time: O(V + E); space: O(V). BFS finds shortest paths by number of edges in unweighted graphs.

Back to top ↑