🔒 Pro · Component 2 · 2.3.1 Algorithms
2.3.1d Graph Traversal: BFS and DFS in Algorithms
OCR H446 · A Level Computer Science · ~20 min read
Notes
Video
Slides
Worksheet
Quiz

Graphs — Recap

A graph consists of vertices (nodes) connected by edges. Graphs can be directed or undirected, weighted or unweighted. Graph traversal algorithms visit every vertex systematically. The two fundamental approaches are BFS (Breadth-First Search) and DFS (Depth-First Search).

Example graph (undirected) used throughout this lesson:

A - B - D
|   |
C - E

Adjacency list: A:[B,C], B:[A,D,E], C:[A,E], D:[B], E:[B,C]

Breadth-First Search (BFS)

BFS explores a graph level by level — first all neighbours of the start node, then their neighbours, and so on. It uses a FIFO queue and a visited set to avoid revisiting nodes.

Algorithm

function BFS(graph, start)
    queue ← [start]
    visited ← {start}
    order ← []
    while queue is not empty
        node ← dequeue(queue)    // take from front (FIFO)
        order.append(node)
        for neighbour in graph[node]
            if neighbour not in visited then
                visited.add(neighbour)
                enqueue(queue, neighbour)
            endif
        next neighbour
    endwhile
    return order
endfunction

Worked Trace — BFS from A

StepQueue (front→rear)VisitedProcess
Start[A]{A}Initialise
1[B, C]{A,B,C}Dequeue A; enqueue B, C
2[C, D, E]{A,B,C,D,E}Dequeue B; enqueue D, E (A already visited)
3[D, E]{A,B,C,D,E}Dequeue C; E already visited; no new nodes
4[E]{A,B,C,D,E}Dequeue D; B already visited
5[]{A,B,C,D,E}Dequeue E; all neighbours visited

BFS order: A → B → C → D → E

Complexity: O(V + E) where V = vertices, E = edges. Every vertex and edge is processed exactly once.

BFS Applications

  • Shortest path (unweighted): BFS guarantees the fewest edges between source and any node it visits first — used in GPS routing for unweighted graphs, social network "degrees of connection".
  • Web crawlers: start from a page, explore all links at depth 1 first, then depth 2, etc.
  • Peer-to-peer networks: finding all nodes within k hops.
  • Garbage collection: finding all reachable objects from roots.

Depth-First Search (DFS)

DFS explores a graph by going as deep as possible along each branch before backtracking. It uses a LIFO stack (or the call stack for recursion) and a visited set.

Algorithm (Iterative — uses explicit stack)

function DFS_iterative(graph, start)
    stack ← [start]
    visited ← {}
    order ← []
    while stack is not empty
        node ← pop(stack)        // take from top (LIFO)
        if node not in visited then
            visited.add(node)
            order.append(node)
            for neighbour in graph[node]
                if neighbour not in visited then
                    push(stack, neighbour)
                endif
            next neighbour
        endif
    endwhile
    return order
endfunction

Algorithm (Recursive — uses call stack)

function DFS_recursive(graph, node, visited)
    if visited is None then visited ← {}
    visited.add(node)
    process(node)
    for neighbour in graph[node]
        if neighbour not in visited then
            DFS_recursive(graph, neighbour, visited)
        endif
    next neighbour
endfunction

Worked Trace — DFS from A (iterative)

StepStack (top→)VisitedProcess
Start[A]{}Initialise
1[B, C]{A}Pop A; visit A; push B, C
2[B, A, D, E]{A,C}Pop C; visit C; push A, E (top→E)
3[B, A, D, B, C]{A,C,E}Pop E; visit E; push B, C
4[B, A, D, B]{A,C,E}Pop C — already visited, skip
5[B, A, D]{A,B,C,E}Pop B; visit B; push A,D,E
6[B, A]{A,B,C,D,E}Pop D; visit D
7-[]{A,B,C,D,E}Pop remaining — all visited

DFS order (one possible): A → C → E → B → D (order depends on neighbour ordering)

Complexity: O(V + E) — same as BFS. Every vertex and edge is processed exactly once.

DFS Applications

  • Topological sort: ordering of tasks where some must precede others (dependency resolution, build systems).
  • Cycle detection: check if a graph contains a cycle — if DFS revisits a node already in the current path, a cycle exists.
  • Maze solving: DFS explores one path fully before backtracking to try another.
  • Finding connected components: run DFS from unvisited nodes to identify all components.

BFS vs DFS Comparison

FeatureBFSDFS
Data structureQueue (FIFO)Stack (LIFO) or recursion
Exploration orderLevel by level (breadth-first)As deep as possible first
Finds shortest path?Yes (unweighted graphs)No — not guaranteed
Time complexityO(V + E)O(V + E)
Space complexityO(V) — queue can hold entire levelO(V) — stack/call stack depth
Memory usageHigher (wide graphs)Lower (deep graphs)
Use forShortest path, level-order, social graphsTopological sort, cycle detection, mazes
Exam tip: BFS → Queue (FIFO), level-by-level, shortest path unweighted. DFS → Stack (LIFO) or recursion, deep first, not shortest path. Both are O(V+E). The visited set prevents infinite loops on cyclic graphs. Always mark a node as visited before/when enqueuing (BFS) or when first popped (DFS).
Exam tip: Exam traces often ask you to show the queue/stack contents and visited set at each step. For BFS, nodes are added to visited when enqueued (not when dequeued) — otherwise the same node could be enqueued multiple times. For iterative DFS, check visited when popped.
✓ Notes completed!
Video coming soon
Click to advance · Arrow keys also work
Click slide or press arrow keys to navigate

Worksheet — 2.3.1d Graph Traversal: BFS & DFS

8 questions · 24 marks · instantly marked

Q1State the data structure used by BFS and the data structure used by DFS. Explain why each is appropriate for its traversal pattern.[4 marks]
✓ Mark scheme
BFS uses a Queue (FIFO) [1]. Appropriate because BFS explores level by level — all nodes at distance 1 before distance 2, etc. FIFO ensures nodes are processed in the order they were discovered (first discovered = first processed), maintaining the level-by-level order [1]. DFS uses a Stack (LIFO) for iterative version, or the call stack for recursive version [1]. Appropriate because DFS goes as deep as possible. LIFO ensures the most recently discovered node is explored next, sending the search deeper down one branch before exploring siblings [1].
Q2Given the graph: A connected to B, C; B connected to A, D; C connected to A, D; D connected to B, C. Trace BFS starting from A. Show the queue and visited set at each step.[4 marks]
✓ Mark scheme
Start: queue=[A], visited={A} [0.5]
Step 1: dequeue A; visit A; enqueue B,C → queue=[B,C], visited={A,B,C} [1]
Step 2: dequeue B; visit B; D not visited → enqueue D → queue=[C,D], visited={A,B,C,D} [1]
Step 3: dequeue C; visit C; D already visited → queue=[D], visited={A,B,C,D} [1]
Step 4: dequeue D; visit D; B,C already visited → queue=[], visited={A,B,C,D} [0.5]
BFS order: A, B, C, D
Q3Why does BFS guarantee the shortest path (in terms of edges) from the start node to any other node in an unweighted graph?[2 marks]
✓ Mark scheme
BFS explores nodes level by level — all nodes at edge-distance k are fully explored before any node at distance k+1 [1]. Therefore, the first time BFS reaches a node, it has done so via the minimum number of edges (fewest hops). Any later path to the same node would be at least as long. This property only holds for unweighted graphs (equal edge costs). For weighted graphs, Dijkstra's algorithm is needed [1].
Q4Trace DFS from A on the same graph (A:[B,C], B:[A,D], C:[A,D], D:[B,C]) using an iterative approach with a stack. Show stack and visited at each step.[3 marks]
✓ Mark scheme
Start: stack=[A], visited={} [0.5]
Pop A: visit A; push B,C → stack=[B,C], visited={A} [0.5]
Pop C: visit C; push A(visited),D → stack=[B,D], visited={A,C} [0.5]
Pop D: visit D; push B(not visited),C(visited) → stack=[B,B], visited={A,C,D} [0.5]
Pop B: visit B; push A(v),D(v) → stack=[B], visited={A,B,C,D} [0.5]
Pop B: already visited, skip → done [0.5]
DFS order: A, C, D, B (order may vary)
Q5Why is a visited set essential in graph traversal? What happens if you omit it for a cyclic graph?[2 marks]
✓ Mark scheme
The visited set tracks which nodes have already been explored, preventing them from being processed again [1]. Without a visited set on a cyclic graph: the traversal would loop infinitely — e.g., A→B→A→B→A... because each node re-adds its neighbours (including the one we came from). The queue/stack would grow without bound and the program would never terminate [1].
Q6Give two applications of DFS that are NOT suitable for BFS. Explain why DFS is better for each.[3 marks]
✓ Mark scheme
Any 2 of: Topological sort — requires DFS's post-order processing (add node to result after all its descendants are explored). BFS doesn't naturally produce topological order [1]. Cycle detection — DFS can detect back edges (edges to ancestors in the DFS tree), which indicate cycles. BFS can detect cycles too, but DFS's stack-based exploration makes it more natural for directed graphs [1]. Maze solving — DFS follows one path to the end (or dead end) before backtracking, exactly matching how maze solving works. BFS would need to explore all paths of the same length simultaneously, which is less intuitive and uses more memory in deep mazes [1].
Q7State the time complexity of both BFS and DFS in terms of V (vertices) and E (edges). Justify why the complexity includes both V and E.[2 marks]
✓ Mark scheme
Both BFS and DFS: O(V + E) [1]. Justification: every vertex is visited exactly once (O(V) total vertex processing); for each vertex, all its edges are examined to find unvisited neighbours (O(E) total edge processing across all vertices). Using an adjacency list, sum of all adjacency lists = E edges. Total operations: V vertex visits + E edge checks = O(V + E) [1].
Q8Write the recursive DFS algorithm in pseudocode. Identify the base case and the recursive case.[4 marks]
✓ Mark scheme
function DFS(graph, node, visited)
  visited.add(node) [0.5]
  process(node) // e.g., print or append to order [0.5]
  for neighbour in graph[node]
    if neighbour not in visited then [0.5]
      DFS(graph, neighbour, visited) // recursive call [0.5]
    endif
  next neighbour
endfunction
Base case: when all neighbours of node are already in visited (no unvisited neighbours exist) — the for loop body never triggers the recursive call → recursion terminates naturally [1]. Recursive case: call DFS on each unvisited neighbour [1].
Topic Quiz
1 of 15
You scored
out of 15
🎯

Mini Test — 2.3.1d Graph Traversal

  • 10 questions · 10 marks · 10 minutes
  • 5 MCQ + 5 short answer
Card 1 of 15
Click to reveal
🎉
Complete!
TermDefinition
🔒
Pro Content
Subscribe to access all 69 OCR H446 A Level lessons.
£7.99/month
or £59/year
Subscribe now →
← 2.3.1c Bubble, Insertion & Merge Sort 2.3.1 Algorithms Next: 2.3.1e Tree Traversal & Dijkstra →