20. Graph Representations — Adjacency Matrix & List
606 words
3 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# 20. Graph Representations — Adjacency Matrix & List > **What problem does this solve?** Graphs model relationships: road networks, social media connections, web pages.

20. Graph Representations — Adjacency Matrix & List
What problem does this solve? Graphs model relationships: road networks, social media connections, web pages. Before we can traverse or analyze graphs, we need to store them. Two standard representations offer different trade-offs.
1. Graph Terminology
| Term | Definition |
|---|---|
| Vertex (node) | Entity in the graph |
| Edge | Connection between two vertices |
| Directed graph | Edges have direction (u→v ≠ v→u) |
| Undirected graph | Edges are bidirectional |
| Weighted graph | Edges have costs/weights |
| Path | Sequence of vertices connected by edges |
| Cycle | Path that starts and ends at same vertex |
| Adjacent | Two vertices connected by an edge |
| Degree | Number of edges incident to a vertex |
(Diagram)
2. Adjacency Matrix
How It Works
A |V| × |V| matrix where
matrix[u][v] = 1 (or weight) if there's an edge u→v.
(Diagram)Implementation
python# runnable import numpy as np class GraphMatrix: """Graph using adjacency matrix (NumPy).""" def __init__(self, vertices, directed=False): self.n = vertices self.directed = directed # 3D matrix: [is_edge, weight] per cell self.M = np.zeros(shape=(vertices, vertices, 2)) def add_edge(self, u, v, weight=1): self.M[u, v, 0] = 1 self.M[u, v, 1] = weight if not self.directed: self.M[v, u, 0] = 1 self.M[v, u, 1] = weight def has_edge(self, u, v): return self.M[u, v, 0] == 1 def get_weight(self, u, v): return self.M[u, v, 1] if self.has_edge(u, v) else float('inf') def neighbors(self, u): """Return list of neighbors of vertex u.""" return [v for v in range(self.n) if self.M[u, v, 0] == 1] # Test gm = GraphMatrix(4, directed=False) gm.add_edge(0, 1, 5) gm.add_edge(0, 2, 2) gm.add_edge(1, 2, 3) gm.add_edge(2, 3, 1) print(f"Edge 0→1: {gm.has_edge(0, 1)}") # True print(f"Neighbors of 0: {gm.neighbors(0)}") # [1, 2] print(f"Weight 0→2: {gm.get_weight(0, 2)}") # 2.0
3. Adjacency List
How It Works
For each vertex, store a list of its neighbors (and optionally edge weights).
python# runnable class GraphList: """Graph using adjacency list (dictionary).""" def __init__(self, vertices=None, directed=False): self.directed = directed self.adj = {} # vertex → list of (neighbor, weight) if vertices: for v in range(vertices): self.adj[v] = [] def add_vertex(self, v): if v not in self.adj: self.adj[v] = [] def add_edge(self, u, v, weight=1): self.add_vertex(u) self.add_vertex(v) self.adj[u].append((v, weight)) if not self.directed: self.adj[v].append((u, weight)) def neighbors(self, u): return self.adj.get(u, []) def vertices(self): return list(self.adj.keys()) # Test gl = GraphList(directed=False) gl.add_edge(0, 1, 5) gl.add_edge(0, 2, 2) gl.add_edge(1, 2, 3) gl.add_edge(2, 3, 1) print(f"Adjacency list: {gl.adj}") # {0: [(1, 5), (2, 2)], 1: [(0, 5), (2, 3)], # 2: [(0, 2), (1, 3), (3, 1)], 3: [(2, 1)]}
4. Comparison
| Feature | Adjacency Matrix | Adjacency List |
|---|---|---|
| Space | (O( | V |
| Edge lookup | (O(1)) | (O(degree)) |
| List all neighbors | (O( | V |
| Add edge | (O(1)) | (O(1)) |
| Remove edge | (O(1)) | (O(degree)) |
| Best for | Dense graphs ( | E |
Rule of thumb: Use adjacency list for most real-world graphs (they're sparse). Use matrix when |V| ≤ 1000 and you need fast edge lookups.
Practice Questions
Q1. How much memory does an adjacency matrix use for |V| = 1000? What about an adjacency list with |E| = 5000?
Q2. Why is adjacency list preferred for BFS/DFS on sparse graphs?
Q3. Convert this edge list to adjacency list: [(0,1), (0,2), (1,2), (2,3)]
Q4. What's the maximum number of edges in a directed graph with n vertices?
AnswersA1. Matrix: 1000 × 1000 = 1,000,000 entries. List: 1000 lists with 10,000 total entries (each edge stored twice for undirected).A2. BFS/DFS explores neighbors. With adjacency list, iterating over neighbors of a vertex takes O(degree), not O(|V|). For sparse graphs, this is much faster.A3.{0: [1, 2], 1: [0, 2], 2: [0, 1, 3], 3: [2]}A4. (n(n-1)) (no self-loops). Each vertex can connect to every other vertex. Join Discord Previous19. Heap SortNext21. BFS & DFS — Graph Traversals