Algorithm Search⌘K

Proofs: Depth-first search

These are the human proofs of Depth-first search, at full length.

Depth-first search visits each vertex once and finds the reachable set full

This proof shows: correctness, termination.

Let G=(V,E)G = (V, E) be a directed or undirected graph with nn vertices, where an undirected edge is in both adjacency lists. We say that a call DFS-VISIT(u)(u) is active from its start at line L5 to its end at line L10. A call that starts while another call is active ends before that call ends, because calls nest as on any call stack.

Lemma 1 (one call for each vertex). During DFS(G)(G), DFS-VISIT(u)(u) is called exactly one time for each u∈Vu \in V. A vertex is white before its call, gray while its call is active, and black after it. The run stops, and the times d[u]d[u] and f[u]f[u] over all uu are the numbers 1,2,…,2n1, 2, \ldots, 2n, each used one time, with d[u]<f[u]d[u] < f[u].

Proof. Only line L5 makes a vertex gray, only line L10 makes it black, and no line makes a vertex white after line L1. So the colour of each vertex goes from white to gray to black, and never back. A call DFS-VISIT(u)(u) is made at line L4 or line L9 only after the test that uu is white, and its first line makes uu gray. So there is at most one call for each uu. The loop of line L3 tests each vertex, and it calls DFS-VISIT on each vertex that is still white. So after the loop, each vertex had its call, and there are exactly nn calls.

Each call does a finite loop over Adj[u]\mathrm{Adj}[u] besides its own nested calls, and there are only nn calls in total, so the run stops. Each call increases timetime by 1 at line L5 and by 1 at line L10, and it stores the new value in d[u]d[u] and f[u]f[u]. Nothing else changes timetime, which starts at 0. So the 2n2n increases give the values 1,…,2n1, \ldots, 2n, each stored one time. In each call, line L5 runs before line L10, so d[u]<f[u]d[u] < f[u]. □\square

Lemma 2 (the gray vertices). At each moment, the gray vertices are exactly the vertices with an active call. If they are g1,…,gkg_1, \ldots, g_k in the order of their calls, then g1g_1 was called at line L4, and π[gi+1]=gi\pi[g_{i+1}] = g_i for 1≤i<k1 \le i < k.

Proof. The first statement is Lemma 1. The active calls are the calls on the call stack, from the bottom g1g_1 to the top gkg_k. The bottom call comes from line L4, because a call from line L9 is made while another call is active. For i<ki < k, the call of gi+1g_{i+1} was made at line L9 of the call of gig_i, which is the call just below it on the stack, and line L8 set π[gi+1]=gi\pi[g_{i+1}] = g_i just before. □\square

Lemma 3 (the forest). When DFS(G)(G) stops, $\pi[v] = $ NIL exactly for the vertices vv called at line L4. For each other vertex vv, (π[v],v)∈E(\pi[v], v) \in E and d[π[v]]<d[v]d[\pi[v]] < d[v]. So the edges (π[v],v)(\pi[v], v) form a forest, and each of its trees has one root called at line L4.

Proof. Line L1 sets each parent to NIL, and only line L8 changes π[v]\pi[v]. Line L8 runs for vv only when vv is white, just before the call of vv at line L9, so it runs at most one time for each vv, and exactly for the vertices called at line L9. These are the vertices not called at line L4, by Lemma 1. At that moment, the call of u=π[v]u = \pi[v] is active and v∈Adj[u]v \in \mathrm{Adj}[u], so (u,v)∈E(u, v) \in E, and d[u]d[u] is already set while d[v]d[v] is set later, so d[u]<d[v]d[u] < d[v]. Along the chain v,π[v],π[π[v]],…v, \pi[v], \pi[\pi[v]], \ldots the discovery time decreases strictly, so the chain has no repeated vertex and it ends at a vertex with parent NIL. So the parent edges contain no cycle, and each vertex is joined to exactly one root. □\square

Lemma 4 (reachability). Let all vertices be white, and call DFS-VISIT(s)(s). Let DD be the set of vertices discovered during this call, and let RR be the set of vertices reachable from ss. Then D=RD = R.

Proof. First D⊆RD \subseteq R. We use induction on the order of discovery. The first vertex of DD is s∈Rs \in R. A later vertex v∈Dv \in D is discovered at line L9 in the call of some vertex uu with v∈Adj[u]v \in \mathrm{Adj}[u]. This call of uu is active during the call of ss, so it started during the call of ss or it is the call of ss. So u∈Du \in D, and uu was discovered before vv. By the induction hypothesis u∈Ru \in R, and the edge (u,v)(u, v) gives v∈Rv \in R.

Now R⊆DR \subseteq D. Assume that some w∈Rw \in R is not in DD. Take a path s=x0,x1,…,xk=ws = x_0, x_1, \ldots, x_k = w in GG, and let ii be the least index with xi∉Dx_i \notin D. Then i≥1i \ge 1 because s∈Ds \in D, and xi−1∈Dx_{i-1} \in D. The call of xi−1x_{i-1} started during the call of ss, or is it, so it ends before the call of ss ends. In this call, the loop of line L6 reaches xix_i, because xi∈Adj[xi−1]x_i \in \mathrm{Adj}[x_{i-1}]. If xix_i is white at that moment, line L9 calls DFS-VISIT(xi)(x_i) during the call of ss, so xi∈Dx_i \in D. If xix_i is not white, it was discovered before that moment. All vertices were white when the call of ss began, so xix_i was discovered after that, and before the moment, which is during the call of ss. So xi∈Dx_i \in D again. Both cases contradict the choice of ii. □\square

Theorem. DFS(G)(G) stops after nn calls of DFS-VISIT. It gives each vertex one discovery time and one finish time, the times are 1,…,2n1, \ldots, 2n, and the parent pointers form a forest of edges of GG whose roots are the vertices picked at line L4. A single call DFS-VISIT(s)(s) on a graph with all vertices white discovers exactly the vertices reachable from ss, and the parent pointers then give a path of GG from ss to each of them.

Proof. The first part is Lemmas 1 and 3. The second part is Lemma 4. For the paths, follow the parent pointers from a vertex v∈Dv \in D. By Lemma 3, each step goes back along an edge of GG to a vertex with a smaller discovery time, and by the first half of the proof of Lemma 4 each such vertex is in DD. The chain ends at the only vertex of DD with parent NIL, which is ss. Read backwards, it is a path from ss to vv. ■\blacksquare

The white-path theorem extends Lemma 4 to each call of DFS-VISIT, also when some vertices are no longer white, and the parenthesis theorem describes how the intervals [d[u],f[u]][d[u], f[u]] nest.

Back to Depth-first search