Representing graphs
In one line
A graph is nodes plus edges, and you store it as an adjacency list, an adjacency matrix, an edge list, or implicitly as a grid, choosing by size, density and the queries you need.
What it is
A graph is a set of vertices (nodes) connected by edges. Edges can be undirected (a friendship goes both ways) or directed (a follow goes one way), and weighted (a road has a length) or unweighted. A tree is a special graph: connected, with no cycles, and exactly V - 1 edges.
An adjacency list stores, for each vertex, the list of its neighbours. It uses O(V + E) memory and lists a vertex's neighbours in O(degree) time. An adjacency matrix is a V by V grid where cell [u][v] is 1 (or the weight) if an edge exists. It uses O(V^2) memory but checks any edge in O(1).
An edge list is just a list of (u, v, weight) tuples, which is how most interview inputs arrive and what Kruskal's algorithm wants. A grid is an implicit graph: each cell is a node and its up, down, left and right neighbours are edges, computed on the fly instead of stored.
Why interviewers ask
Before any BFS, DFS or shortest path, you must turn the input into something you can traverse. Interviewers watch whether you build an adjacency list quickly and correctly (both directions for undirected edges), recognise hidden graphs such as grids, word ladders or course prerequisites, and can justify list vs matrix with complexity.
How it works
- 1Identify the vertices (people, cells, words, states) and what makes an edge between them.
- 2Decide directed or undirected and weighted or not. Undirected edges are stored twice, once from each end.
- 3Choose a structure: adjacency list for most sparse graphs (E much smaller than V^2), matrix for small dense graphs or when you need O(1) edge checks.
- 4Build it in one pass over the edge list: for each (u, v), append v to adj[u] (and u to adj[v] if undirected). For weights, append (v, w).
- 5For grids, do not build anything: generate neighbours with direction offsets and a bounds check while traversing.
Worked example · One small graph, three representations
- Input: 4 nodes, undirected edges [[0,1], [0,2], [1,2], [2,3]].
- Adjacency list: 0: [1, 2]; 1: [0, 2]; 2: [0, 1, 3]; 3: [2]. That is 8 entries, 2 per edge.
- Adjacency matrix (rows 0 to 3): row 0 = 0 1 1 0; row 1 = 1 0 1 0; row 2 = 1 1 0 1; row 3 = 0 0 1 0. It is symmetric because edges are undirected, and uses 16 cells for 4 edges.
- Is there an edge 1-3? Matrix: look at [1][3] = 0, one step. List: scan 1's neighbours [0, 2], not found.
- Neighbours of 2? List: read [0, 1, 3] directly. Matrix: scan all 4 cells of row 2. With a million nodes, that row scan is a million checks per vertex.
Picture it
An adjacency list is each person's phone contacts: short and personal. An adjacency matrix is a giant seating chart of every possible pair in a city with a tick where two people know each other: instant to check, but mostly empty paper.
Reference code
from collections import defaultdict
def build_adj(n, edges, directed=False):
adj = [[] for _ in range(n)]
for u, v in edges:
adj[u].append(v)
if not directed:
adj[v].append(u) # undirected: store both ways
return adj
def build_weighted(edges):
adj = defaultdict(list) # works for non-integer node names
for u, v, w in edges:
adj[u].append((v, w))
return adj
def build_matrix(n, edges):
m = [[0] * n for _ in range(n)]
for u, v in edges:
m[u][v] = m[v][u] = 1
return m
def grid_neighbors(grid, r, c):
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = r + dr, c + dc
if 0 <= nr < len(grid) and 0 <= nc < len(grid[0]):
yield nr, ncbuild_adj is the line you will write in nearly every graph problem. build_weighted stores (neighbour, weight) pairs for Dijkstra. build_matrix shows the O(V^2) alternative. grid_neighbors generates edges of an implicit grid graph on demand.
Key patterns
Edge list to adjacency list
When: Input is n and a list of pairs (most LeetCode graph problems)
How: adj = [[] for _ in range(n)] or defaultdict(list); append both directions if undirected.
Implicit grid graph
When: Islands, mazes, flood fill, rotting oranges
How: Use direction offsets ((1,0), (-1,0), (0,1), (0,-1)) and bounds checks; never materialise edges.
Hidden graph from states
When: Word ladder, open the lock, minimum genetic mutation
How: Each state is a node; generate neighbours by applying every allowed one-step change, often with a wildcard pattern map to speed up lookup.
Adjacency matrix for dense or small graphs
When: Floyd-Warshall, V up to a few hundred, frequent edge checks
How: dist = [[inf] * n for _ in range(n)]; set dist[u][v] = w and dist[i][i] = 0.
Real-world use
Social networks
Facebook's TAO store keeps objects (users, posts) and typed associations between them, effectively a huge adjacency list, so loading a user's friends is one lookup of their association list.
Maps and navigation
Road networks in Google Maps and OpenStreetMap are directed weighted graphs: intersections are nodes, road segments are edges with travel time, stored as adjacency lists because each intersection connects to only a few roads.
Package managers
npm, pip and apt model packages as nodes and dependencies as directed edges, then traverse the graph to resolve install order and detect cycles.
Graph databases
Neo4j stores each node with direct pointers to its relationships, so traversing to neighbours does not require an index lookup, which is ideal for multi-hop queries like friends of friends.
In a project
You build a who-should-I-follow feature for a developer community with 2 million users and an average of 150 follows each.
- Model users as vertices and follows as directed edges; store an adjacency list (user id to list of followed ids) in a key-value store or in memory.
- Reject a matrix early: 2 million squared is 4 trillion cells, while the list holds about 300 million entries.
- Also keep the reverse list (followers) so both who-I-follow and who-follows-me are one lookup.
- Generate suggestions by walking two hops out from a user and counting how often each candidate appears.
- Store neighbour lists as sets when you need fast is-following checks.
Impact: Memory drops from impossible to a few gigabytes, and each suggestion needs only the neighbour lists of one user's follows, keeping requests in the low milliseconds.
Trade-offs
Use it when
- Adjacency list: sparse graphs, traversals (BFS, DFS, Dijkstra), large V
- Adjacency matrix: small or dense graphs, O(1) edge checks, all-pairs algorithms
- Edge list: algorithms that sort or scan edges (Kruskal, Bellman-Ford)
- Implicit graph: grids and state spaces too large or too regular to store
Avoid it when
- Matrix for large sparse graphs: O(V^2) memory and O(V) to list neighbours
- Plain lists when you need frequent edge-exists checks on high-degree nodes: use a list of sets
- Building the full graph of a huge state space (word ladder over a big dictionary) when neighbours can be generated lazily
Cost: Adjacency list: O(V + E) space, O(deg) to list or check neighbours. Matrix: O(V^2) space, O(1) edge check, O(V) to list neighbours. Edge list: O(E) space.
Common mistakes
- Adding an undirected edge in only one direction, so traversal misses half the graph.
- Using a dictionary keyed only by nodes that appear in edges, then forgetting isolated nodes that have no edges.
- Getting the direction backwards for prerequisites: [a, b] often means b must come before a.
- Choosing a matrix for 100,000 nodes: 10 billion cells will not fit in memory.
- Building explicit edges for a grid instead of computing neighbours on the fly.
Interview questions
Quick revision
- Graph = vertices + edges; directed or undirected; weighted or not.
- Adjacency list: O(V + E) space; the default for interviews.
- Matrix: O(V^2) space, O(1) edge check; only for small or dense graphs.
- Undirected edge = append both ways.
- Weighted list stores (neighbour, weight).
- Grids are implicit graphs: 4 offsets plus bounds check.
- A tree is a connected acyclic graph with V - 1 edges.
Check yourself
1. How much memory does an adjacency list need for a graph with V vertices and E edges?
2. Which operation is O(1) with an adjacency matrix but not with an adjacency list?
3. For undirected edges [[0,1],[1,2]], what is the adjacency list?
4. In a grid of R rows and C columns used as a graph, how many edges does each interior cell have with 4-directional moves?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.