Yasir Explains/Algorithms/Graphs and Basic Traversal Techniques/Representation of Graphs
Graphs and Basic Traversal Techniques

Representation of Graphs

On this page

What is a graph?Three standard representationsAdjacency matrix — the pictureAdjacency list — the workhorseEdge list — the simplest representationStep-by-step: building the adjacency list from inputPseudocodeMemory and operation complexity
Graphs and Basic Traversal Techniques

Representation of Graphs

How to store a graph in memory: adjacency matrix vs. adjacency list vs. edge list, directed/undirected, weighted/unweighted — and how the right choice changes the running time of every algorithm that follows.

What is a graph?

A graph is a pair G = (V, E) where V is a set of vertices (also called nodes) and E is a set of edges that connect pairs of vertices. Graphs model anything that has things and relationships: roads between cities, friendships between people, dependencies between tasks, links between web pages, pixels in an image.

Two flavors matter most:

  • Undirected — an edge {u, v} is a symmetric connection (e.g. a friendship).
  • Directed — an edge (u, v) points from u to v and may have no reverse (e.g. a Twitter follow).

Edges can also be weighted (carry a number like distance or cost) or unweighted (just present or absent).

Example — undirected graph with 5 vertices:

Example
(1)───(2)
│ ╲ │
│ ╲ │
(4)───(3)
│
(5)

Vertices: {1, 2, 3, 4, 5} · Edges: {1-2, 1-3, 1-4, 2-3, 3-4, 3-5}.

Three standard representations

Given the same graph, we can store it in (at least) three different ways. Each one trades memory for query speed.

1. Adjacency matrix — a V × V boolean (or weight) matrix A where A[u][v] = 1 if there is an edge from u to v.

2. Adjacency list — for each vertex u, a list of vertices reachable from u in one edge.

3. Edge list — a flat list of edges (u, v[, w]), with no per-vertex index.

The right choice depends on density (E vs. V²) and on which queries you run most:

QuestionAdjacency matrixAdjacency listEdge list
Is u–v an edge?O(1)O(deg(u))O(E)
Iterate neighbors of uO(V)O(deg(u))O(E)
MemoryΘ(V²)Θ(V + E)Θ(E)
Best fordense graphs, fast edge checksmost contest problemsKruskal, sorting edges

Adjacency matrix — the picture

For the 5-vertex undirected graph above, the adjacency matrix is symmetric (A[u][v] = A[v][u]):

Example
1 2 3 4 5
┌─────────────────┐
1 │ 0 1 1 1 0 │
2 │ 1 0 1 0 0 │
3 │ 1 1 0 1 1 │
4 │ 1 0 1 0 0 │
5 │ 0 0 1 0 0 │
└─────────────────┘

Reading row 3 across, the 1s sit in columns 1, 2, 4, 5 — exactly the neighbors of vertex 3.

When to use it: the graph is dense (close to V² edges), or you need O(1) hasEdge(u, v) lookups (e.g. Floyd–Warshall, dense max-flow).

When not to use it: sparse graphs with V = 10⁵ — a 10⁵×10⁵ matrix holds 10¹⁰ entries, which is out of the question both in memory and time.

Adjacency list — the workhorse

For the same graph, the adjacency list stores only the existing edges:

Example
1 → [2, 3, 4]
2 → [1, 3]
3 → [1, 2, 4, 5]
4 → [1, 3]
5 → [3]

Total entries = 2·|E| = 12 for an undirected graph (each edge appears twice — once at each endpoint). For a sparse graph this is dramatically smaller than a matrix.

This is the default representation for almost every BFS, DFS, Dijkstra, Bellman–Ford, Kruskal, and Prim implementation you will write. Pretty much every algorithm that scans neighbors of a vertex benefits from O(deg(u)) iteration.

Weighted version: store (neighbor, weight) pairs instead of just neighbors:

Example
1 → [(2, 4), (3, 1), (4, 2)]

Edge list — the simplest representation

An edge list is just an array of edges. Each entry is (u, v) for unweighted graphs or (u, v, w) for weighted ones.

Example
edges = [
(1, 2), (1, 3), (1, 4),
(2, 3),
(3, 4), (3, 5)
]

It is the natural format when the algorithm processes edges as a whole rather than walking neighbors of a vertex — Kruskal's MST is the canonical example: sort edges by weight, then scan them in order. Reading the input as an edge list and converting to an adjacency list when needed is a common pattern.

Step-by-step: building the adjacency list from input

Suppose the input is the number of vertices n = 5, the number of edges m = 6, and a list of edges. We build the adjacency list in m steps.

Input edges: (1,2), (1,3), (1,4), (2,3), (3,4), (3,5)

Example
Step 0 — empty lists
1 → [] 2 → [] 3 → [] 4 → [] 5 → []
Step 1 — read (1, 2) push 2 onto 1's list, push 1 onto 2's list
1 → [2] 2 → [1] 3 → [] 4 → [] 5 → []
Step 2 — read (1, 3)
1 → [2, 3] 2 → [1] 3 → [1] 4 → [] 5 → []
Step 3 — read (1, 4)
1 → [2, 3, 4] 2 → [1] 3 → [1] 4 → [1] 5 → []
Step 4 — read (2, 3)
1 → [2, 3, 4] 2 → [1, 3] 3 → [1, 2] 4 → [1] 5 → []
Step 5 — read (3, 4)
1 → [2, 3, 4] 2 → [1, 3] 3 → [1, 2, 4] 4 → [1, 3] 5 → []
Step 6 — read (3, 5)
1 → [2, 3, 4] 2 → [1, 3] 3 → [1, 2, 4, 5] 4 → [1, 3] 5 → [3]

For a directed graph, drop the second push — only add v to u's list, not the other way around.

Pseudocode

Building any representation is a single pass over the input edges — the only decision is where each edge gets recorded. The pseudocode covers both the adjacency list and the adjacency matrix; the C++ tab is a complete program that reads n, m, and the edge list, builds all three representations, and prints the adjacency list.

1// Build an adjacency list from m edges (undirected, unweighted)
2INPUT: n vertices labeled 1..n, m edges
3OUTPUT: adj[1..n], where adj[u] is the list of neighbors of u
4
5for v = 1 to n
6 adj[v] = empty list
7
8repeat m times
9 read edge (u, v)
10 append v to adj[u]
11 append u to adj[v] // omit this line for a directed graph
12
13// Build an adjacency matrix instead
14for i = 1 to n
15 for j = 1 to n
16 A[i][j] = 0
17repeat m times
18 read edge (u, v)
19 A[u][v] = 1
20 A[v][u] = 1 // omit for directed

Memory and operation complexity

OperationMatrixListEdge list
Build from inputΘ(V² + E)Θ(V + E)Θ(E)
hasEdge(u, v)Θ(1)Θ(deg(u))Θ(E)
Iterate neighbors(u)Θ(V)Θ(deg(u))Θ(E)
Add edgeΘ(1)Θ(1) amortizedΘ(1)
Remove edgeΘ(1)Θ(deg(u))Θ(E)
MemoryΘ(V²)Θ(V + E)Θ(E)

Rule of thumb: if V ≤ 1000, either representation is fine. For larger V, prefer the adjacency list unless the graph is genuinely dense (E close to V²/2).

Complexity Analysis

Time Complexity

Building the adjacency list is Θ(V + E); the matrix is Θ(V² + E). Query times depend on representation (see the table above).

Space Complexity

Adjacency list: Θ(V + E). Adjacency matrix: Θ(V²). Edge list: Θ(E).

For sparse graphs (E ≪ V²) the adjacency list wins on both memory and neighbor-iteration time. For dense graphs or O(1) edge-existence queries, prefer the matrix.

Growth Rate Comparison

n (input size)O(1)O(log n)O(n)O(n log n)O(n²)