LESSON 02 · UNIT 3 AOS 1 & 2 GRAPHS & TRAVERSALS

Graph Foundations, Structural Variants & BFS/DFS Traversals

A graph G=(V,E)G = (V, E) is a mathematical structure consisting of a finite set of vertices VV (nodes) and a set of edges EE (links) connecting pairs of vertices.

01 · MATHEMATICAL GRAPH THEORY

formal graph definitions & metrics.

Unlike linear containers where input size is governed by a single parameter nn, graph algorithms depend on two independent structural parameters: the vertex count ∣V∣|V| and the edge count ∣E∣|E|. Formally, in set-builder notation:

V={v1,v2,…,vn},E={(vi,vj)∣vi,vj∈V∧vi is connected to vj}V = \{v_1, v_2, \dots, v_n\}, \qquad E = \{(v_i, v_j) \mid v_i, v_j \in V \land v_i \text{ is connected to } v_j\}

In an undirected graph, the edge relation EE is a symmetric binary relation ((vi,vj)∈E  ⟺  (vj,vi)∈E(v_i, v_j) \in E \iff (v_j, v_i) \in E), whereas in a directed graph (digraph), EE is non-symmetric and edges are ordered pairs representing one-way arcs.

CORE GRAPH TERMINOLOGY & FORMAL DEFINITIONS (HESS NOTES)
CONCEPT NOTATION FORMAL DEFINITION
Order of a Graph ∣V∣|V| The cardinality of the vertex set VV; the total number of nodes in GG.
Size of a Graph ∣E∣|E| The cardinality of the edge set EE; the total number of links in GG.
Vertex Degree (Undirected) deg⁡(v)\deg(v) The number of edges incident to vertex vv. In a simple graph, max⁡(deg⁡(vi))=∣V∣−1\max(\deg(v_i)) = |V| - 1. By the Handshaking Lemma, ∑v∈Vdeg⁡(v)=2∣E∣\sum_{v \in V} \deg(v) = 2|E|.
In-Degree & Out-Degree deg⁡−(v),deg⁡+(v)\deg^-(v), \deg^+(v) In a directed graph, deg⁡−(v)\deg^-(v) counts incoming directed edges (u,v)(u, v) (head at vv) and deg⁡+(v)\deg^+(v) counts outgoing edges (v,u)(v, u) (tail at vv).
Distance & Diameter d(u,v),diam(G)d(u, v), \text{diam}(G) Distance d(u,v)d(u, v) is the shortest path length between uu and vv. The diameter is the maximum shortest-path distance over all vertex pairs: max⁡u,v∈Vd(u,v)\max_{u,v \in V} d(u, v). If GG is disconnected, its diameter is ∞\infty.
Transitive Closure G+=(V,E+)G^+ = (V, E^+) The reachability graph where edge (u,v)∈E+(u, v) \in E^+ exists if and only if there is a directed path of length ≥1\ge 1 from uu to vv in GG.
02 · STRUCTURAL TAXONOMY

graph variants, density & trees.

The structural constraints placed on EE determine the maximum possible number of edges, the formal Graph Density (density(G)=∣E∣/max⁡(∣E∣)\text{density}(G) = |E| / \max(|E|)), and which representation (Adjacency List vs Adjacency Matrix) is asymptotically superior.

Simple Undirected Max Edges: max⁡(∣E∣)=∣V∣(∣V∣−1)2Simple Directed Max Edges: max⁡(∣E∣)=∣V∣(∣V∣−1)\text{Simple Undirected Max Edges: } \max(|E|) = \frac{|V|(|V| - 1)}{2} \qquad \text{Simple Directed Max Edges: } \max(|E|) = |V|(|V| - 1)
Simple Undirected Density: density(G)=2∣E∣∣V∣(∣V∣−1)Simple Directed Density: density(G)=∣E∣∣V∣(∣V∣−1)\text{Simple Undirected Density: } \text{density}(G) = \frac{2|E|}{|V|(|V| - 1)} \qquad \text{Simple Directed Density: } \text{density}(G) = \frac{|E|}{|V|(|V| - 1)}
SIMPLE, MULTIGRAPH & COMPLETE EDGE CONSTRAINTS
  • Simple Graph: Contains no self-loops (v,v)(v, v) and no parallel edges between the same pair of vertices. Maximum degree per node is max⁡(deg⁡(vi))=∣V∣−1\max(\deg(v_i)) = |V| - 1.
  • Multigraph: Permits multiple parallel edges between the same pair of vertices (and optionally self-loops).
  • Complete Graph (KnK_n): A simple undirected graph in which every pair of distinct vertices is connected by a unique edge (∣E∣=(n2)=n(n−1)2|E| = \binom{n}{2} = \frac{n(n-1)}{2}, density(Kn)=1\text{density}(K_n) = 1).
CONNECTED, BRIDGES & DAGS TOPOLOGY
  • Connected Graph: An undirected graph with a path between every pair of vertices (∣E∣≥∣V∣−1|E| \ge |V| - 1).
  • Bridge (Cut-Edge): An edge e∈Ee \in E whose removal increases the number of connected components in GG. An edge is a bridge iff it does not lie on any simple cycle.
  • DAG (Directed Acyclic Graph): A directed graph containing no directed cycles. Used to model task dependencies, prerequisite chains, and version histories.
SPARSE VS DENSE GRAPHS REPRESENTATION CHOICE
  • Sparse Graph (density(G)≈0\text{density}(G) \approx 0): Approaches a tree (∣E∣≪∣V∣2|E| \ll |V|^2, typically ∣E∣=O(∣V∣)|E| = \mathcal{O}(|V|)). Best stored as an Adjacency List using Θ(∣V∣+∣E∣)\Theta(|V| + |E|) space.
  • Dense Graph (density(G)≈1\text{density}(G) \approx 1): Approaches a complete graph (∣E∣≈∣V∣2|E| \approx |V|^2). Best stored as an Adjacency Matrix (∣V∣×∣V∣|V| \times |V| table) using Θ(∣V∣2)\Theta(|V|^2) space with O(1)\mathcal{O}(1) edge lookup.
TREES & SPANNING TREES ACYCLIC MINIMALITY
  • Tree: A connected, acyclic undirected graph. Any tree with ∣V∣|V| vertices has exactly ∣E∣=∣V∣−1|E| = |V| - 1 edges, and every edge is a bridge.
  • Spanning Tree: A subgraph T=(V,ET)T = (V, E_T) of a connected graph G=(V,E)G = (V, E) that includes all vertices VV and forms a tree (ET⊆EE_T \subseteq E with ∣ET∣=∣V∣−1|E_T| = |V| - 1).

VCE Pseudocode Conventions & The 3 Core Control Structures

Before writing graph traversal algorithms, the HESS notes standardise VCE Algorithmics Pseudocode around three fundamental control structures, using <- for variable assignment, Algorithm for the main procedure, Function for helper subroutines, and Display / Return for output:

THE 3 CORE STRUCTURES SEQUENCE · SELECTION · ITERATION
  • 1. Sequence: Linear top-to-bottom execution of statements and assignments (x <- 0).
  • 2. Selection: Conditional branching via If ... Then ... Else if ... Else ... End if.
  • 3. Iteration: Unbounded loops (While <cond> do ... End do / Do ... While <cond>) and bounded loops (For each x in S do ... End do).
CANONICAL VCE PSEUDOCODE EXAMPLE HESS ALGORITHM MEAN(LIST)
Algorithm Mean(list):
    sum <- 0
    For each element in list do:
        sum <- sum + element
    End do
    mean <- sum / length(list)
    Return mean
End Algorithm
03 · LEVEL-ORDER GRAPH EXPLORATION

breadth-first search (bfs).

Breadth-First Search (BFS) traverses a graph layer-by-layer outward from a source vertex ss using a FIFO Queue. Because it explores all vertices at distance kk before discovering any vertex at distance k+1k+1, BFS is guaranteed to find the shortest path in an unweighted graph (minimum number of edges).

  • 3 Ways to Implement Node Marking (Visited State): To prevent infinite loops on cycles, visited vertices are marked using (1) a node attribute (e.g., v.visited = True), (2) a Boolean array indexed by vertex ID (visited[v] = True), or (3) a Hash Table / Set ADT (visited.add(v)).
  • BFS Distance-Labelling Procedure (s→ts \to t Shortest Unweighted Path): Set level counter i=0i = 0 and label source ss with 00. In each pass, find all unlabelled vertices adjacent to any vertex labelled ii, mark them with i+1i + 1, and increment ii until target tt is marked.
  • BFS Tree Edges & Back-Edges Reconstruct GG: The edges traversed to discover unvisited vertices form a spanning tree (the BFS Tree, or a BFS Forest if repeated across an outer loop For each Node start_node in G on a disconnected graph). Edges leading to already-visited vertices are back-edges (or cross-edges); adding all non-tree back-edges back to the BFS/DFS spanning tree recovers the exact original graph GG!
ALGORITHM · BREADTH-FIRST SEARCH (BFS) VCE PSEUDOCODE
Algorithm BFS(G, root):
    let Q be a queue
    let T be a new Graph // BFS Tree
    mark root as visited
    Q.enqueue(root)
    While Q is not empty do:
        v <- Q.dequeue()
        For each w in G.adjacent(v) do:
            If w is not visited then:
                mark w as visited
                add edge (v, w) to T
                Q.enqueue(w)
            End If
        End For
    End While
    Return T
End Algorithm
IMPLEMENTATION · PYTHON NETWORKX BFS HESS VAULT CODE
import networkx as nx
from collections import deque

def bfs_tree(G: nx.Graph, startNode):
    visited = set([startNode])
    queue = deque([startNode])
    T = nx.Graph()
    T.add_node(startNode)

    while queue:
        v = queue.popleft()
        for w in G.neighbors(v):
            if w not in visited:
                visited.add(w)
                T.add_edge(v, w)
                queue.append(w)
    return T
BFS ASYMPTOTIC COMPLEXITY TIME & SPACE BOUNDS

With an Adjacency List, every vertex is enqueued and dequeued at most once (O(∣V∣)\mathcal{O}(|V|)), and every edge is inspected across the adjacency lists (O(∣E∣)\mathcal{O}(|E|)), yielding Θ(∣V∣+∣E∣)\Theta(|V| + |E|) time complexity and O(∣V∣)\mathcal{O}(|V|) auxiliary space for the Queue and visited set.

04 · DEEP BRANCH EXPLORATION

depth-first search (dfs): iterative vs recursive.

Depth-First Search (DFS) plunges as deep as possible along each branch before backtracking. It can be written iteratively using an explicit LIFO Stack or recursively using the program call stack. In Python iterative DFS, calling neighbours.reverse() before pushing onto the stack ensures neighbours are popped and visited in the exact same natural order as recursive DFS. If the graph is disconnected, wrapping DFS in an outer loop For each Node start_node in G restarts traversal from unvisited vertices to produce a DFS Forest. Beyond basic traversal, DFS powers Topological Sorting (on DAGs), finding Strongly Connected Components, and testing kk-partiteness (such as 2-colouring bipartite graphs).

ITERATIVE DFS · EXPLICIT STACK VCE PSEUDOCODE
Algorithm DFS_Iterative(G, start):
    let S be a stack
    S.push(start)
    While S is not empty do:
        v <- S.pop()
        If v is not visited then:
            mark v as visited
            For each w in G.adjacent(v) do:
                If w is not visited then:
                    S.push(w)
                End If
            End For
        End If
    End While
End Algorithm
RECURSIVE DFS · CALL STACK VCE PSEUDOCODE
Algorithm DFS_Recursive(G, v):
    mark v as visited
    For each u in G.adjacent(v) do:
        If u is not visited then:
            DFS_Recursive(G, u)
        End If
    End For
End Algorithm

DFS Time Complexity: Best, Average & Worst Case (Adjacency List vs Matrix)

In VCE Algorithmics exams, deriving the exact complexity of DFS requires accounting for both how the graph is stored and its edge density:

DFS TIME COMPLEXITY BY GRAPH REPRESENTATION & TOPOLOGY (HESS NOTES)
REPRESENTATION BEST CASE (TREE: ∣E∣=∣V∣−1|E| = |V|-1) AVERAGE CASE WORST CASE (COMPLETE: ∣E∣=Θ(∣V∣2)|E| = \Theta(|V|^2))
Adjacency List Θ(∣V∣)\Theta(|V|) (since ∣E∣=∣V∣−1|E| = |V|-1) Θ(∣V∣+∣E∣)\Theta(|V| + |E|) Θ(∣V∣2)\Theta(|V|^2) (when ∣E∣≈∣V∣2|E| \approx |V|^2)
Adjacency Matrix Θ(∣V∣2)\Theta(|V|^2) Θ(∣V∣2)\Theta(|V|^2) Θ(∣V∣2)\Theta(|V|^2)
  • Why Adjacency List is Θ(∣V∣+∣E∣)\Theta(|V| + |E|): Initialising vertices takes Θ(∣V∣)\Theta(|V|). Iterating through G.adjacent(v) across all visited vertices sums to ∑v∈Vdeg⁡(v)=2∣E∣\sum_{v \in V} \deg(v) = 2|E|. For a tree (∣E∣=∣V∣−1|E| = |V|-1), this simplifies to Θ(∣V∣)\Theta(|V|); for a complete graph, Θ(∣V∣2)\Theta(|V|^2).
  • Why Adjacency Matrix is Always Θ(∣V∣2)\Theta(|V|^2): Finding the neighbours of each vertex vv requires scanning an entire matrix row of length ∣V∣|V| regardless of how few edges vv actually has, costing ∣V∣×∣V∣=Θ(∣V∣2)|V| \times |V| = \Theta(|V|^2) in Best, Average, and Worst cases.
05 · STRUCTURAL VERIFICATION ALGORITHMS

connectedness check & cycle detection.

Two essential applications of DFS in the HESS syllabus are testing whether an undirected graph is connected and detecting whether an undirected graph contains a cycle.

1. Graph Connectedness Check (Counting Visited Nodes)

Initialise a counter count <- 0 and run DFS (or BFS) starting from a single arbitrary vertex v0∈Vv_0 \in V, incrementing count each time a vertex is marked visited. After that single pass terminates, check whether count == |V| (or equivalently, whether the outer loop For each Node start_node in G executes only once). If count == |V|, GG is connected; otherwise, GG is disconnected.

BFS/DFS Spanning Tree Edges vs Cycle-Inducing Back-Edge Comparison of BFS level-order distances and DFS cycle detection showing solid tree edges and a dashed back-edge to a visited non-ancestor node. BFS TREE + NON-TREE EDGE back/cross edge s:0 A:1 B:1 C:2 Tree + Back-Edges = Graph G DFS CYCLE CHECK (u != ancestor) u != ancestor (CYCLE!) tree edge u v w At w: parent is v, visited neighbour u != v
FIGURE 02 · BFS LEVEL DISTANCES, SPANNING TREE EDGES (SOLID) & CYCLE-INDUCING BACK-EDGES (DASHED)

2. Undirected Cycle Detection (The ancestor Parameter)

In an undirected graph, every edge {u,v}\{u, v\} appears in both uu's adjacency list and vv's adjacency list. When DFS moves from parent uu to child vv, examining vv's neighbours will immediately see uu as already visited! However, going immediately back to the parent uu is not a cycle.

Therefore, undirected DFS cycle detection must pass an ancestor (parent) parameter: a cycle exists if and only if DFS encounters an adjacent vertex ww that is already visited AND w≠ancestorw \neq \text{ancestor}.

ALGORITHM · UNDIRECTED CYCLE DETECTION VIA DFS HESS PSEUDOCODE WITH ANCESTOR CHECK
Algorithm HasCycle(G):
    mark all vertices in G as unvisited

    Function DFS_Cycle(v, ancestor):
        mark v as visited
        For each u in G.adjacent(v) do:
            If u is not visited then:
                If DFS_Cycle(u, v) == True then:
                    Return True
                End If
            Else If u != ancestor then:
                // Visited neighbour that is NOT our immediate parent -> back-edge cycle!
                Return True
            End If
        End For
        Return False
    End Function

    For each v in G.vertices do:
        If v is not visited then:
            If DFS_Cycle(v, null) == True then:
                Return True
            End If
        End If
    End For
    Return False
End Algorithm