Chapter 41: Advanced Graph Structure and Specialized Graph Problems
41.08Intermediate / Advanced4 topics18 min2 interactive animations

Spectral and random walks

Random walks turn a graph into a Markov chain, whose stationary distribution is PageRank; the Laplacian matrix encodes the same graph algebraically, and its eigenvectors reveal clusters.

Learning goals

  • Write the transition matrix of a random walk and iterate it
  • Explain PageRank, damping and dangling pages
  • Build the graph Laplacian and read the number of components from it
  • Describe how the Fiedler vector splits a graph into clusters

Prerequisites

Matrix-vector productsGraph representations (Chapter 32)Power iteration idea (probability basics)

Imagine a surfer who clicks random links: where do they spend their time? That single question defines PageRank. The same graph seen as a matrix tells you how many pieces it has and where to cut it, using only linear algebra.

41.08.1

Random walk transition

A **random walk** on a directed graph moves from the current vertex u to one of its out-neighbours chosen uniformly at random. The **transition matrix** P has P[u][v] = 1 / outdeg(u) for every edge u → v and 0 otherwise, so every row sums to 1. A vertex with no out-edges (a **dangling** vertex) is handled by letting it jump to any vertex with equal probability.

If dist is a row vector of probabilities over the vertices, one step of the walk gives dist · P. Applying this over and over (**power iteration**) moves the distribution toward a **stationary distribution** π that satisfies π = π · P: once there, one more step changes nothing.

Open the animation with the preset “Pure random walk converges”. The graph 0→1, 1→2, 2→0, 0→2 settles at 0.4, 0.2, 0.4 after a few dozen rounds. The preset “Pure walk on a 2-cycle never settles” shows why a plain walk is not enough: on 0↔1, 0↔2 the distribution alternates between uniform and 2/3, 1/6, 1/6 forever, because the graph is periodic. Damping removes this problem.

Java
1// p[u][v] = probability that a random walker standing at u moves to v next (a vertex without out-edges jumps anywhere)
2static double[][] transitionMatrix(int n, List<Integer>[] out) {
3 double[][] p = new double[n][n];
4 for (int u = 0; u < n; u++) {
5 if (out[u].isEmpty()) { Arrays.fill(p[u], 1.0 / n); continue; }
6 for (int v : out[u]) p[u][v] += 1.0 / out[u].size();
7 }
8 return p;
9}
10static double[] step(double[] dist, double[][] p) { // the distribution after one step: dist * P
11 int n = dist.length;
12 double[] next = new double[n];
13 for (int u = 0; u < n; u++) for (int v = 0; v < n; v++) next[v] += dist[u] * p[u][v];
14 return next;
15}

Random walks and PageRank: power iteration on the transition matrix

Interactive · edit the input

Your input

Try

directed graph · 4 vertices · 5 edges

0

0.2500

1

0.2500

2

0.2500

3

0.2500

Highest rank this roundRank above the uniform 1/n

rank of each page

0.25000
0.25001
0.25002
0.25003
Setup

Imagine a surfer who, with probability 0.85, follows a random link of the current page and otherwise jumps to a uniformly random page. The rank of a page is the long-run fraction of time spent there. Power iteration starts from the uniform vector 1/4 and applies the transition rule once per round.

Step 1 / 32
Java
1// out[v] = successors of v; d is the damping factor in (0, 1]; returns the rank vector after the given number of rounds (it sums to 1).
2// A vertex without out-edges is "dangling": its rank is shared equally by all vertices.
3static double[] pageRank(int n, List<Integer>[] out, double d, int rounds) {
4 double[] rank = new double[n];
5 Arrays.fill(rank, 1.0 / n); // start from the uniform distribution
6 for (int round = 0; round < rounds; round++) {
7 double[] next = new double[n];
8 double dangling = 0;
9 for (int u = 0; u < n; u++) {
10 if (out[u].isEmpty()) { dangling += rank[u]; continue; }
11 for (int v : out[u]) next[v] += d * rank[u] / out[u].size(); // u hands its rank equally to its out-neighbours
12 }
13 for (int v = 0; v < n; v++) next[v] += d * dangling / n + (1 - d) / n; // dangling mass and teleportation, spread evenly
14 rank = next;
15 }
16 return rank;
17}

Live state

0 round

n

4

damping

0.85

rounds

30

dangling

0

No condition evaluated on this step
Execution trace (dry run table)Show 32 steps
StepLineStateWhat happens
1L3n=4 damping=0.85 rounds=30 dangling=0Imagine a surfer who, with probability 0.85, follows a random link of the current page and otherwise jumps to a uniformly random page. The rank of a page is the long-run fraction of time spent there. Power iteration starts from the uniform vector 1/4 and applies the transition rule once per round.
2L14round=1 delta=0.6375 sum=1Round 1: the ranks still sum to 1. Total change from the previous round: 0.6375.
3L14round=2 delta=0.541875 sum=1Round 2: the ranks still sum to 1. Total change from the previous round: 0.5419.
4L14round=3 delta=0.460594 sum=1Round 3: the ranks still sum to 1. Total change from the previous round: 0.4606.
5L14round=4 delta=0.195752 sum=1Round 4: the ranks still sum to 1. Total change from the previous round: 0.1958.
6L14round=5 delta=0.083195 sum=1Round 5: the ranks still sum to 1. Total change from the previous round: 0.0832.
7L14round=6 delta=0.070716 sum=1Round 6: the ranks still sum to 1. Total change from the previous round: 0.0707.
8L14round=7 delta=0.060108 sum=1Round 7: the ranks still sum to 1. Total change from the previous round: 0.0601.
9L14round=8 delta=0.025546 sum=1Round 8: the ranks still sum to 1. Total change from the previous round: 0.0255.
10L14round=9 delta=0.010857 sum=1Round 9: the ranks still sum to 1. Total change from the previous round: 0.0109.
11L14round=10 delta=0.009228 sum=1Round 10: the ranks still sum to 1. Total change from the previous round: 0.0092.
12L14round=11 delta=0.007844 sum=1Round 11: the ranks still sum to 1. Total change from the previous round: 0.0078.
13L14round=12 delta=0.003334 sum=1Round 12: the ranks still sum to 1. Total change from the previous round: 0.0033.
14L14round=13 delta=0.001417 sum=1Round 13: the ranks still sum to 1. Total change from the previous round: 0.0014.
15L14round=14 delta=0.001204 sum=1Round 14: the ranks still sum to 1. Total change from the previous round: 0.0012.
16L14round=15 delta=0.001024 sum=1Round 15: the ranks still sum to 1. Total change from the previous round: 0.0010.
17L14round=16 delta=0.000435 sum=1Round 16: the ranks still sum to 1. Total change from the previous round: 0.0004.
18L14round=17 delta=0.000185 sum=1Round 17: the ranks still sum to 1. Total change from the previous round: 0.0002.
19L14round=18 delta=0.000157 sum=1Round 18: the ranks still sum to 1. Total change from the previous round: 0.0002.
20L14round=19 delta=0.000134 sum=1Round 19: the ranks still sum to 1. Total change from the previous round: 0.0001.
21L14round=20 delta=0.000057 sum=1Round 20: the ranks still sum to 1. Total change from the previous round: 0.0001 (essentially converged).
22L14round=21 delta=0.000024 sum=1Round 21: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
23L14round=22 delta=0.000021 sum=1Round 22: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
24L14round=23 delta=0.000017 sum=1Round 23: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
25L14round=24 delta=0.000007 sum=1Round 24: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
26L14round=25 delta=0.000003 sum=1Round 25: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
27L14round=26 delta=0.000003 sum=1Round 26: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
28L14round=27 delta=0.000002 sum=1Round 27: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
29L14round=28 delta=0.000001 sum=1Round 28: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
30L14round=29 delta=0 sum=1Round 29: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
31L14round=30 delta=0 sum=1Round 30: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
32L16return=0.3725 0.1958 0.3941 0.0375 ranks=0.3725 0.1958 0.3941 0.0375After 30 rounds the ranks are 0.3725, 0.1958, 0.3941, 0.0375. The damping factor makes the walk forget its start, so the iteration converges geometrically at rate 0.85 per round.
41.08.2

PageRank intuition

**PageRank** models a surfer who, with probability d (the damping factor, classically 0.85), follows a random link from the current page and, with probability 1 − d, jumps to a page chosen uniformly at random. A page's rank is the long-run fraction of time the surfer spends on it: pages with many incoming links from important pages rank high.

The teleportation makes the chain strongly connected and aperiodic, so the stationary distribution exists, is unique and power iteration converges **geometrically at rate d per round**: after 60 rounds with d = 0.85 the error is about 0.85^60 ≈ 6 · 10^-5. Each round hands d · rank[u] / outdeg(u) to every out-neighbour and adds (1 − d) / n (plus the dangling mass) to everyone.

On the animation's default web 0→1, 0→2, 1→2, 2→0, 3→2 with d = 0.85 the ranks after 30 rounds are 0.3725, 0.1958, 0.3941, 0.0375. Page 2 is the best ranked: it has three in-links. Page 3 has no in-links at all, so its rank is exactly the teleportation share (1 − d) / n = 0.15 / 4 = 0.0375. With d = 0.5 page 3 gets 0.125. The dangling-page preset shows the leaked rank being redistributed.

Problem of the plain walkFix in PageRank
dangling pages leak probabilitytheir rank is shared by all pages
periodic graphs oscillateteleportation breaks the periodicity
several components: no unique answerteleportation connects everything
slow convergencerate d per round: smaller d is faster but forgets the links
Java
1// out[v] = successors of v; d is the damping factor in (0, 1]; returns the rank vector after the given number of rounds (it sums to 1).
2// A vertex without out-edges is "dangling": its rank is shared equally by all vertices.
3static double[] pageRank(int n, List<Integer>[] out, double d, int rounds) {
4 double[] rank = new double[n];
5 Arrays.fill(rank, 1.0 / n); // start from the uniform distribution
6 for (int round = 0; round < rounds; round++) {
7 double[] next = new double[n];
8 double dangling = 0;
9 for (int u = 0; u < n; u++) {
10 if (out[u].isEmpty()) { dangling += rank[u]; continue; }
11 for (int v : out[u]) next[v] += d * rank[u] / out[u].size(); // u hands its rank equally to its out-neighbours
12 }
13 for (int v = 0; v < n; v++) next[v] += d * dangling / n + (1 - d) / n; // dangling mass and teleportation, spread evenly
14 rank = next;
15 }
16 return rank;
17}

Worst

O((V + E) · rounds)

Space

O(V)

Random walks and PageRank: power iteration on the transition matrix

Interactive · edit the input

Your input

Try

directed graph · 4 vertices · 5 edges

0

0.2500

1

0.2500

2

0.2500

3

0.2500

Highest rank this roundRank above the uniform 1/n

rank of each page

0.25000
0.25001
0.25002
0.25003
Setup

Imagine a surfer who, with probability 0.85, follows a random link of the current page and otherwise jumps to a uniformly random page. The rank of a page is the long-run fraction of time spent there. Power iteration starts from the uniform vector 1/4 and applies the transition rule once per round.

Step 1 / 32
Java
1// out[v] = successors of v; d is the damping factor in (0, 1]; returns the rank vector after the given number of rounds (it sums to 1).
2// A vertex without out-edges is "dangling": its rank is shared equally by all vertices.
3static double[] pageRank(int n, List<Integer>[] out, double d, int rounds) {
4 double[] rank = new double[n];
5 Arrays.fill(rank, 1.0 / n); // start from the uniform distribution
6 for (int round = 0; round < rounds; round++) {
7 double[] next = new double[n];
8 double dangling = 0;
9 for (int u = 0; u < n; u++) {
10 if (out[u].isEmpty()) { dangling += rank[u]; continue; }
11 for (int v : out[u]) next[v] += d * rank[u] / out[u].size(); // u hands its rank equally to its out-neighbours
12 }
13 for (int v = 0; v < n; v++) next[v] += d * dangling / n + (1 - d) / n; // dangling mass and teleportation, spread evenly
14 rank = next;
15 }
16 return rank;
17}

Live state

0 round

n

4

damping

0.85

rounds

30

dangling

0

No condition evaluated on this step
Execution trace (dry run table)Show 32 steps
StepLineStateWhat happens
1L3n=4 damping=0.85 rounds=30 dangling=0Imagine a surfer who, with probability 0.85, follows a random link of the current page and otherwise jumps to a uniformly random page. The rank of a page is the long-run fraction of time spent there. Power iteration starts from the uniform vector 1/4 and applies the transition rule once per round.
2L14round=1 delta=0.6375 sum=1Round 1: the ranks still sum to 1. Total change from the previous round: 0.6375.
3L14round=2 delta=0.541875 sum=1Round 2: the ranks still sum to 1. Total change from the previous round: 0.5419.
4L14round=3 delta=0.460594 sum=1Round 3: the ranks still sum to 1. Total change from the previous round: 0.4606.
5L14round=4 delta=0.195752 sum=1Round 4: the ranks still sum to 1. Total change from the previous round: 0.1958.
6L14round=5 delta=0.083195 sum=1Round 5: the ranks still sum to 1. Total change from the previous round: 0.0832.
7L14round=6 delta=0.070716 sum=1Round 6: the ranks still sum to 1. Total change from the previous round: 0.0707.
8L14round=7 delta=0.060108 sum=1Round 7: the ranks still sum to 1. Total change from the previous round: 0.0601.
9L14round=8 delta=0.025546 sum=1Round 8: the ranks still sum to 1. Total change from the previous round: 0.0255.
10L14round=9 delta=0.010857 sum=1Round 9: the ranks still sum to 1. Total change from the previous round: 0.0109.
11L14round=10 delta=0.009228 sum=1Round 10: the ranks still sum to 1. Total change from the previous round: 0.0092.
12L14round=11 delta=0.007844 sum=1Round 11: the ranks still sum to 1. Total change from the previous round: 0.0078.
13L14round=12 delta=0.003334 sum=1Round 12: the ranks still sum to 1. Total change from the previous round: 0.0033.
14L14round=13 delta=0.001417 sum=1Round 13: the ranks still sum to 1. Total change from the previous round: 0.0014.
15L14round=14 delta=0.001204 sum=1Round 14: the ranks still sum to 1. Total change from the previous round: 0.0012.
16L14round=15 delta=0.001024 sum=1Round 15: the ranks still sum to 1. Total change from the previous round: 0.0010.
17L14round=16 delta=0.000435 sum=1Round 16: the ranks still sum to 1. Total change from the previous round: 0.0004.
18L14round=17 delta=0.000185 sum=1Round 17: the ranks still sum to 1. Total change from the previous round: 0.0002.
19L14round=18 delta=0.000157 sum=1Round 18: the ranks still sum to 1. Total change from the previous round: 0.0002.
20L14round=19 delta=0.000134 sum=1Round 19: the ranks still sum to 1. Total change from the previous round: 0.0001.
21L14round=20 delta=0.000057 sum=1Round 20: the ranks still sum to 1. Total change from the previous round: 0.0001 (essentially converged).
22L14round=21 delta=0.000024 sum=1Round 21: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
23L14round=22 delta=0.000021 sum=1Round 22: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
24L14round=23 delta=0.000017 sum=1Round 23: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
25L14round=24 delta=0.000007 sum=1Round 24: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
26L14round=25 delta=0.000003 sum=1Round 25: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
27L14round=26 delta=0.000003 sum=1Round 26: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
28L14round=27 delta=0.000002 sum=1Round 27: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
29L14round=28 delta=0.000001 sum=1Round 28: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
30L14round=29 delta=0 sum=1Round 29: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
31L14round=30 delta=0 sum=1Round 30: the ranks still sum to 1. Total change from the previous round: 0.0000 (essentially converged).
32L16return=0.3725 0.1958 0.3941 0.0375 ranks=0.3725 0.1958 0.3941 0.0375After 30 rounds the ranks are 0.3725, 0.1958, 0.3941, 0.0375. The damping factor makes the walk forget its start, so the iteration converges geometrically at rate 0.85 per round.
41.08.3

Graph Laplacian

The **graph Laplacian** of an undirected simple graph is L = D − A, where D is the diagonal matrix of degrees and A the adjacency matrix. Each row sums to 0, so the all-ones vector is an eigenvector with eigenvalue 0. For a path of three vertices L = [[1, −1, 0], [−1, 2, −1], [0, −1, 1]] with eigenvalues 0, 1 and 3.

The Laplacian measures smoothness: for any vector x, xᵀ L x = Σ over edges (x_u − x_v)². This makes L positive semidefinite, with all eigenvalues non-negative, and it explains the main structural fact: **the multiplicity of the eigenvalue 0 equals the number of connected components**. A vector that is constant on each component has zero energy, and nothing else does.

The code builds L and counts the zero eigenvalues as n − rank(L) by Gaussian elimination with partial pivoting. This is not faster than a BFS for counting components, but it shows the algebra at work, and the same matrix gives more: the second-smallest eigenvalue (the **algebraic connectivity**) is positive exactly when the graph is connected and is large for well-connected graphs.

FactMeaning
L = D − Adegrees on the diagonal, −1 for every edge
x^T L x = Σ (x_u − x_v)²energy of x over the edges; L is positive semidefinite
multiplicity of eigenvalue 0number of connected components
second-smallest eigenvalue λ₂algebraic connectivity; positive iff connected
Java
1static double[][] laplacian(int n, int[][] edges) { // L = D - A of an undirected simple graph
2 double[][] l = new double[n][n];
3 for (int[] e : edges) {
4 l[e[0]][e[0]]++;
5 l[e[1]][e[1]]++;
6 l[e[0]][e[1]]--;
7 l[e[1]][e[0]]--;
8 }
9 return l;
10}
11static int componentsFromLaplacian(double[][] l) { // the number of zero eigenvalues is n - rank(L), and it equals the number of components
12 int n = l.length, rank = 0;
13 double[][] m = new double[n][];
14 for (int i = 0; i < n; i++) m[i] = l[i].clone();
15 for (int col = 0; col < n && rank < n; col++) { // Gaussian elimination with partial pivoting
16 int pivot = rank;
17 for (int r = rank + 1; r < n; r++) if (Math.abs(m[r][col]) > Math.abs(m[pivot][col])) pivot = r;
18 if (Math.abs(m[pivot][col]) < 1e-9) continue;
19 double[] t = m[pivot]; m[pivot] = m[rank]; m[rank] = t;
20 for (int r = rank + 1; r < n; r++) {
21 double f = m[r][col] / m[rank][col];
22 for (int c = col; c < n; c++) m[r][c] -= f * m[rank][c];
23 }
24 rank++;
25 }
26 return n - rank;
27}
41.08.4

Spectral clustering overview

**Spectral clustering** cuts a graph where it is weakly connected, using the eigenvector of the second-smallest Laplacian eigenvalue, the **Fiedler vector**. It minimises Σ (x_u − x_v)² over edges for vectors that are orthogonal to the all-ones vector and have unit length: vertices joined by many edges get similar values. The signs of the Fiedler vector therefore split the vertices into two sides with few edges between them.

To compute it without a linear-algebra library, run **power iteration** on shift · I − L, where shift is at least the largest eigenvalue. That matrix has the same eigenvectors as L with the order of the eigenvalues reversed, so the dominant eigenvector is that of the smallest eigenvalue of L: the all-ones vector. Projecting every iterate orthogonal to the all-ones vector removes it, and the iteration converges to the Fiedler vector. The code does exactly this and returns the side of each vertex.

The verification ran two cliques of 3 to 5 vertices joined by a single edge and checked that the sign split recovers the two cliques; it also checked that a path with an even number of vertices splits into its two halves. For more than two clusters, take the first k eigenvectors, treat each vertex as a point in k dimensions and run k-means. The method has no guarantee of the optimal cut, but the Cheeger inequality bounds how good the sign cut is in terms of the eigenvalue.

Java
1// splits a connected graph in two by the sign of the Fiedler vector: the eigenvector of the second-smallest eigenvalue of L
2static boolean[] spectralBisection(int n, int[][] edges) {
3 double[][] l = laplacian(n, edges);
4 double shift = 0;
5 for (int i = 0; i < n; i++) shift = Math.max(shift, 2 * l[i][i]); // shift >= the largest eigenvalue, so shift*I - L has the same eigenvectors, reversed in order
6 double[] x = new double[n];
7 Random random = new Random(1);
8 for (int i = 0; i < n; i++) x[i] = random.nextDouble() - 0.5;
9 for (int iter = 0; iter < 3000; iter++) { // power iteration on shift*I - L
10 double mean = 0;
11 for (double v : x) mean += v / n;
12 for (int i = 0; i < n; i++) x[i] -= mean; // stay orthogonal to the all-ones vector (eigenvalue 0 of L)
13 double[] y = new double[n];
14 for (int i = 0; i < n; i++) {
15 y[i] = shift * x[i];
16 for (int j = 0; j < n; j++) y[i] -= l[i][j] * x[j];
17 }
18 double norm = 0;
19 for (double v : y) norm += v * v;
20 norm = Math.sqrt(norm);
21 for (int i = 0; i < n; i++) x[i] = y[i] / norm;
22 }
23 boolean[] side = new boolean[n];
24 for (int i = 0; i < n; i++) side[i] = x[i] > 0;
25 return side;
26}

Worst

O(iterations · n²) with a dense matrix

Space

O(n²)

Practice ladder

Now try it yourself

Easy

  • Find Center of Star Graph

Medium

  • Number of Provinces

Advanced

  • Minimum Number of Days to Disconnect Island

Key takeaways

  • Transition matrix rows sum to 1; power iteration moves a distribution toward the stationary one.
  • PageRank = random surfer with teleportation; it converges at rate d per round and gives a link-less page exactly (1 − d) / n.
  • The Laplacian's zero eigenvalues count components; the Fiedler vector's signs give a spectral cut.