Graphs

Represent a graph the way the problem needs it — and notice how many problems are graph problems in disguise.

19of 24
≈ 12Min read
5Parts

Nodes and edges, and four questions

A tree is a graph: connected, undirected, no cycles, exactly n−1 edges. Everything in the tree lessons is a special case of everything in these three.

QuestionThe two answers
Directed?A follows B without B following A — or the edge goes both ways
Weighted?Every edge has a cost (distance, time, price) — or they are all equal
Cyclic?Can you get back where you started? A graph with no cycles and directions is a DAG
Dense or sparse?Close to n² edges, or closer to n

Adjacency list against adjacency matrix

Use an adjacency list. Real graphs — road networks, social graphs, dependency trees, web links — are overwhelmingly sparse: a node has a handful of neighbours, not thousands. A matrix for a million nodes needs a trillion cells to store a few million edges.

js
/* adjacency list — a Map from node to its neighbours */
const graph = new Map([
  ["a", ["b", "c"]],
  ["b", ["d"]],
  ["c", ["d"]],
  ["d", []],
]);

/* adjacency matrix — row i, column j is 1 when i → j */
const matrix = [
  [0, 1, 1, 0],
  [0, 0, 0, 1],
  [0, 0, 0, 1],
  [0, 0, 0, 0],
];
ListMatrix
MemoryO(n + e)O(n²), even for three edges
Is there an edge a→b?O(degree)O(1)
Every neighbour of aO(degree)O(n) — you scan the whole row
Add an edgeO(1)O(1)
Good forSparse graphs — almost everything realDense graphs, and matrix mathematics

Weights, and the shapes they take

Forgetting one direction of an undirected edge is the most common graph bug, and it produces a program that works on some inputs and quietly loses others. Build undirected graphs through a helper that cannot forget.

js
/* weighted: each neighbour carries a cost */
const roads = new Map([
  ["home", [{ to: "shop", cost: 4 }, { to: "park", cost: 2 }]],
  ["park", [{ to: "shop", cost: 1 }]],
  ["shop", []],
]);

/* undirected: add both directions when you build it */
function connect(graph, a, b, cost = 1) {
  if (!graph.has(a)) graph.set(a, []);
  if (!graph.has(b)) graph.set(b, []);
  graph.get(a).push({ to: b, cost });
  graph.get(b).push({ to: a, cost });
}

What is secretly a graph

That last row is the useful habit: many problems that do not look like graphs become easy once you name the nodes and the edges. "What is a state, and what is one move?" is often the whole solution.

ProblemNodesEdges
Routing and mapsJunctionsRoads, weighted by time
Build systems, npm installPackagesDependencies — a DAG, and a cycle is an error
Spreadsheet recalculationCellsReferences — topological order
Social featuresPeopleFollows — directed
A maze, a game boardSquaresLegal moves — unweighted
Word ladders, puzzle statesEvery reachable stateOne legal move
Module imports, deadlocksModules, locksRequires, waits-for — find the cycle

Degree, paths and components

  • Degree — how many edges touch a node. In a directed graph, in-degree and out-degree separately; a node with in-degree 0 is a root of a dependency order.
  • Path — a sequence of nodes joined by edges. A cycle is a path back to where it started.
  • Connected component — a group where everyone can reach everyone. "How many separate islands are there" is exactly this.
  • Topological order — for a DAG, an order where every node comes after everything it depends on. That is what a build system computes.

Practice