Graph Theory: Adjacency Matrix vs List & BFS vs DFS Traversals
Graph Theory: Adjacency Matrix vs List & BFS vs DFS Traversals
A Graph is a non-linear data structure consisting of a finite set of vertices (or nodes) $V$ and a collection of edges $E$ that connect pairs of vertices: $G = (V, E)$. Graphs generalize trees by eliminating the constraints of root nodes, parent-child hierarchies, and cycle-free requirements. Graphs model networks everywhere: social networks (friendships), city road systems, internet web pages (hyperlinks), and dependency packaging systems.
1. Graph Classifications & Terminology
Before writing code, we categorize graphs according to direction, weighting, and cyclic nature:
- 1Directed (Digraph) vs. Undirected:
- Undirected: If an edge exists between node $A$ and node $B$, you can travel bidirectionally: $(A, B) = (B, A)$.
- Directed: Edges have orientation (arrows). $(A \to B)$ allows movement only from $A$ to $B$.
- 1Weighted vs. Unweighted:
- Unweighted: Every edge has uniform cost (effectively $1$).
- Weighted: Edges carry numerical values representing distance, latency, toll fee, or capacity.
- 1Cyclic vs. Acyclic:
- Cyclic: Contains at least one closed walk starting and ending at the same vertex.
- Acyclic (DAG): Directed Acyclic Graph contains zero directed cycles (essential for scheduling, Git commits, and compilers).
- 1Degree:
- In undirected graphs, degree is the number of incident edges.
- In directed graphs, In-Degree is the count of incoming edges; Out-Degree is the count of outgoing edges.
2. Graph Representations: Matrix vs. List
Representation Trade-Off Matrix
| Operation / Metric | Adjacency Matrix | Adjacency List (Standard) | Winner |
|---|---|---|---|
| Space Complexity | $O(V^2)$ regardless of edge count | $O(V + E)$ (or $O(V + 2E)$ for undirected) | List for sparse graphs |
| Add Vertex | $O(V^2)$ (reallocate matrix) | $O(1)$ | List |
| Add Edge | $O(1)$ | $O(1)$ | Tie |
| Query Edge (u, v) | $O(1)$ (matrix[u][v] == 1) | $O(\text{deg}(u))$ (search list) | Matrix |
| Iterate Neighbors of u | $O(V)$ (scan entire row) | $O(\text{deg}(u))$ (scan only actual neighbors) | List |
In technical interviews and real-world software, graphs are overwhelmingly sparse ($E \ll V^2$), making the Adjacency List the universal industry choice.
3. Graph Traversal: BFS vs. DFS
Traversing a graph requires exploring all reachable vertices while maintaining a visited set to prevent infinite loops caused by cycles.
Breadth-First Search (BFS)
- Data Structure: FIFO Queue (
collections.deque). - Strategy: Layer-by-layer exploration (level order).
- Core Property: Computes the shortest path in unweighted graphs (minimum edge count).
Depth-First Search (DFS)
- Data Structure: LIFO Stack (or recursion call stack).
- Strategy: Explore as deep as possible along each branch before backtracking.
- Core Property: Natural for connectivity, maze solving, cycle detection, and topological sorting.
4. Step-by-Step Traversal Trace
Given an undirected graph with nodes 0, 1, 2, 3, 4 and edges: (0,1), (0,2), (1,3), (2,4). Starting at node 0:
BFS Step-by-Step Execution
| Step | Current Node | Visited Set | Queue State (Front -> Back) | Action / Neighbors Added |
|---|---|---|---|---|
| Initial | - | {0} | [0] | Start at root 0 |
| 1 | 0 | {0, 1, 2} | [1, 2] | Dequeue 0, add neighbors 1, 2 |
| 2 | 1 | {0, 1, 2, 3} | [2, 3] | Dequeue 1, add neighbor 3 |
| 3 | 2 | {0, 1, 2, 3, 4} | [3, 4] | Dequeue 2, add neighbor 4 |
| 4 | 3 | {0, 1, 2, 3, 4} | [4] | Dequeue 3, no unvisited neighbors |
| 5 | 4 | {0, 1, 2, 3, 4} | [] | Dequeue 4, no unvisited neighbors. Queue empty! |
BFS Traversal Order: [0, 1, 2, 3, 4]
5. Dual-Language Implementation
Python 3 Implementation
Modern JavaScript (Node.js / ES6) Implementation
6. Complexity Analysis
| Algorithm | Time Complexity | Space Complexity | Primary Best-Use Case |
|---|---|---|---|
| BFS (Adj List) | $O(V + E)$ | $O(V)$ | Finding unweighted shortest path; finding nodes within $k$ hops |
| DFS (Adj List) | $O(V + E)$ | $O(V)$ (recursion depth) | Topological sort; backtracking; finding cycles; flood fill |
| BFS (Adj Matrix) | $O(V^2)$ | $O(V)$ | Dense graphs where $E \approx V^2$ |
Why $O(V + E)$? Every vertex is placed into the queue/stack at most once ($O(V)$). For each vertex, we iterate over all its incident edges ($O(E)$ total edges processed across all adjacency lists).
7. Common Pitfalls & Interview Traps
- 1Disconnected Graphs / Forest: Calling BFS or DFS from a single starting node only visits that connected component. In problems like Number of Connected Components or Number of Islands, you must wrap BFS/DFS in an outer loop over all vertices:
for node in range(V): if node not in visited: bfs(node). - 2Missing
visitedCheck before Queueing: Adding nodes to the BFS queue without marking them visited immediately allows duplicates into the queue, causing catastrophic $O(2^V)$ exponential memory spikes! - 3Call Stack Overflow in DFS: Deep graph traversals (e.g., $10^5$ linear nodes) will crash Python with
RecursionError. In production or deep graphs, convert DFS to an explicit stack loop.
Multiple Choice Questions
visited?Cycle Detection & Topological Sorting (Kahn's BFS & DFS)
Continue learning with hands-on practice, examples, and exercises in the upcoming topic.
Related Lessons
Practice Quiz
Test your understanding of this lesson with 5 questions. Each question has one correct answer.