Discovery and Finish Times
Depth-first search (DFS) does more than record which vertices can be reached. In a recursive search, each vertex has a discovery event, when the search first visits it, and a finish event, when the search has examined all its outgoing edges and returns from its recursive call. The order of these events records the structure of the search: a vertex's recursive call remains active throughout the calls made below it. The Parenthesis Theorem makes this structure visible in the timestamps.
Throughout, graphs are finite and simple. For DFS examples, the graph is directed, and the outgoing neighbors of each vertex are examined in a specified order. The starting vertices (roots) are also considered in a specified order. These orders determine the particular DFS forest and timestamps.
With \(n\) vertices, each vertex is discovered once and finished once, so the event times are the distinct integers \(1,2,\ldots,2n\). In particular, \(d(v)<f(v)\) for every vertex. The convention of using closed intervals is harmless: all discovery and finish times are distinct, so two intervals belonging to different vertices never share an endpoint.
The Parenthesis Theorem
- Their intervals are disjoint: \(f(u)<d(v)\) or \(f(v)<d(u)\).
- Their intervals are strictly nested: \(d(u)<d(v)<f(v)<f(u)\) or \(d(v)<d(u)<f(u)<f(v)\).
Proof. Because the event times are distinct, either \(d(u)<d(v)\) or \(d(v)<d(u)\). First suppose \(d(u)<d(v)\). At the moment \(v\) is discovered, one of two things must be true: the recursive call for \(u\) has already finished, or it is still active. If it has finished, then \(f(u)<d(v)\), so the intervals are disjoint. If it is still active, the call for \(v\) occurs during the call for \(u\). The call for \(v\) must finish before the search can finish the still-active call for \(u\). Therefore \(d(u)<d(v)<f(v)<f(u)\). The same argument with \(u\) and \(v\) interchanged applies when \(d(v)<d(u)\). The cases are mutually exclusive, which proves the result. \(\square\)
The proof uses the defining order of recursive DFS calls: a call cannot finish while a call made within it is still active. This is why the theorem applies to the discovery and finish times of a recursive DFS, rather than to arbitrary pairs of numbers attached to vertices.
Worked Example: Nested Intervals Within One DFS Tree
Let the directed graph have vertex set \(V=\{r,a,b,c,d,e\}\) and edge set \(E=\{ra,rd,ab,ac,bc,de,ec\}\). Examine outgoing neighbors in alphabetical order, with \(r\) as the first root. Thus \(r\)'s neighbors are examined as \(a,d\), \(a\)'s as \(b,c\), and \(e\)'s as \(c\).
The search discovers \(r\), follows \(r\to a\to b\to c\), and finishes \(c\), then \(b\), then \(a\). It next follows \(r\to d\to e\). At \(e\), the edge \(ec\) leads to a vertex already finished; after examining it, DFS finishes \(e\), then \(d\), and finally \(r\). Each row records one event, so its time is the next integer after the previous row.
| Time | Event |
|---|---|
| 1 | Discover \(r\) |
| 2 | Discover \(a\) |
| 3 | Discover \(b\) |
| 4 | Discover \(c\) |
| 5 | Finish \(c\) |
| 6 | Finish \(b\) |
| 7 | Finish \(a\) |
| 8 | Discover \(d\) |
| 9 | Discover \(e\) |
| 10 | Finish \(e\) |
| 11 | Finish \(d\) |
| 12 | Finish \(r\) |
The intervals are \(r:[1,12]\), \(a:[2,7]\), \(b:[3,6]\), \(c:[4,5]\), \(d:[8,11]\), and \(e:[9,10]\). For example, the intervals for \(c\) and \(e\) are disjoint because \(f(c)=5<d(e)=9\). The interval for \(b\) is strictly inside the interval for \(a\), since \(2<3<6<7\).
Check: The DFS tree edges are \(ra,ab,bc,rd,de\). Every child interval is strictly contained in its parent's interval: \(2<3<6<7\) for \(a,b\); \(3<4<5<6\) for \(b,c\); and \(1<8<11<12\) for \(r,d\), then \(8<9<10<11\) for \(d,e\). The times in the table use every integer from \(1\) through \(12\) exactly once.
Intervals Identify Ancestors
In a rooted DFS tree, a vertex \(u\) is an ancestor of \(v\) if \(u\) lies on the unique tree path from the root to \(v\). We count a vertex as its own ancestor. A vertex \(v\) is a descendant of \(u\) under the corresponding convention. For two distinct vertices, ancestry is exactly the strict containment relation between their intervals.
Proof. Suppose first that \(u\) is an ancestor of \(v\). The recursive call for \(v\) is made during the call for \(u\), and it must finish before the call for \(u\) finishes. Also, \(u\) is discovered before \(v\). These facts give \(d(u)<d(v)<f(v)<f(u)\).
Conversely, suppose \(d(u)<d(v)<f(v)<f(u)\). When \(v\) is discovered, the call for \(u\) has started but has not finished, since \(d(u)<d(v)<f(u)\). In recursive DFS, the active calls at any moment form the current recursion stack; each active call other than the last called vertex is an ancestor of that vertex in the DFS tree. Therefore \(u\) is on the active call stack when \(v\) is discovered, and \(u\) is an ancestor of \(v\). This proves both directions. \(\square\)
For a vertex compared with itself, the ancestor relation holds by convention and its interval equals itself; the strict inequalities in the theorem are therefore intended for distinct vertices. For distinct vertices, the Parenthesis Theorem says that if their intervals are not disjoint, one must be an ancestor of the other. Disjoint intervals mean that neither is an ancestor of the other.
Worked Example: Ancestry Across Two DFS Trees
Let the directed graph have vertex set \(V=\{1,2,3,4\}\) and edge set \(E=\{12,34\}\). Take roots in the order \(1,2,3,4\), and examine each vertex's outgoing neighbors in their listed order. Starting at \(1\), DFS discovers \(2\) and finishes \(2\), then finishes \(1\). Neither vertex has an unexamined outgoing edge. The next unvisited root is \(3\); DFS discovers and finishes \(4\), then finishes \(3\).
| Time | Event |
|---|---|
| 1 | Discover \(1\) |
| 2 | Discover \(2\) |
| 3 | Finish \(2\) |
| 4 | Finish \(1\) |
| 5 | Discover \(3\) |
| 6 | Discover \(4\) |
| 7 | Finish \(4\) |
| 8 | Finish \(3\) |
The intervals are \(1:[1,4]\), \(2:[2,3]\), \(3:[5,8]\), and \(4:[6,7]\). Since \(1<2<3<4\), vertex \(1\) is an ancestor of \(2\). Since \(5<6<7<8\), vertex \(3\) is an ancestor of \(4\). The intervals of \(2\) and \(3\) are disjoint because \(3<5\); they belong to different trees, so neither vertex is an ancestor of the other.
Check: The two tree edges are \(12\) and \(34\), exactly the two edges by which the search first discovers a vertex. Each tree edge joins a parent to a vertex with a strictly contained interval. The table has eight events, equal to \(2|V|=2\cdot4=8\), and assigns each event a distinct time from \(1\) to \(8\).
Worked Example: A Later Edge Does Not Make Its Tail an Ancestor
Let the directed graph have vertex set \(V=\{s,p,q,x\}\) and edge set \(E=\{sp,sq,px,qx\}\). Examine \(s\)'s neighbors in the order \(p,q\). Start at \(s\); the only outgoing neighbor of \(p\) is \(x\), and the only outgoing neighbor of \(q\) is also \(x\).
DFS discovers \(s\), then \(p\), then \(x\). It finishes \(x\) and \(p\) before examining \(s\)'s second neighbor \(q\). DFS then discovers and finishes \(q\). When the search examines \(qx\), vertex \(x\) has already finished. The full timestamp sequence is:
| Time | Event |
|---|---|
| 1 | Discover \(s\) |
| 2 | Discover \(p\) |
| 3 | Discover \(x\) |
| 4 | Finish \(x\) |
| 5 | Finish \(p\) |
| 6 | Discover \(q\) |
| 7 | Finish \(q\) |
| 8 | Finish \(s\) |
The intervals are \(s:[1,8]\), \(p:[2,5]\), \(x:[3,4]\), and \(q:[6,7]\). The interval of \(x\) is contained in that of \(p\), so \(p\) is an ancestor of \(x\). The intervals of \(q\) and \(x\) are disjoint because \(4<6\), so \(q\) is not an ancestor of \(x\), despite the edge \(qx\).
Check: The DFS tree edges are \(sp,px,sq\), not \(qx\): by the time \(qx\) is examined, \(x\) is already discovered. The intervals agree with these tree edges: \(1<2<5<8\), \(2<3<4<5\), and \(1<6<7<8\). Again, the event times \(1\) through \(8\) occur exactly once.
Common Pitfall: Overlapping Is Not Enough
A common error is to infer ancestry merely because two intervals overlap. The Parenthesis Theorem requires that overlapping intervals be nested; partial overlap is impossible for a valid recursive DFS trace.
For a concrete incorrect calculation, consider a DFS call on the path \(s\to a\to b\). The assignment \(d(s)=1\), \(d(a)=2\), \(d(b)=3\), \(f(a)=4\), \(f(b)=6\), \(f(s)=8\) would give intervals \([2,4]\) for \(a\) and \([3,6]\) for \(b\). These overlap, but neither contains the other: \(2<3<4<6\). The assignment is wrong because it finishes \(a\) at time \(4\) while the recursive call for its descendant \(b\) is still active. A correct event order is \(d(s)=1,d(a)=2,d(b)=3,f(b)=4,f(a)=5,f(s)=6\), giving intervals \([1,6]\), \([2,5]\), and \([3,4]\). Now each descendant interval is strictly contained in the interval of its ancestors.
The theorem is useful whenever a DFS forest has been computed and one needs to answer ancestry questions from timestamps. For distinct vertices, compare the endpoints: \(d(u)<d(v)<f(v)<f(u)\) certifies that \(u\) is an ancestor of \(v\); \(f(u)<d(v)\) or \(f(v)<d(u)\) certifies that neither is an ancestor of the other. The interval test is valid only for discovery and finish times produced by a consistent DFS execution.
Check Your Understanding
Use the interval statements and the ancestor characterization to answer these questions.
- Suppose distinct vertices \(u,v\) have \(d(u)=4\), \(f(u)=13\), \(d(v)=7\), and \(f(v)=10\). Which vertex is an ancestor of the other?
- Can two distinct vertices in a valid DFS forest have intervals \([2,8]\) and \([6,11]\)? Explain using the Parenthesis Theorem.
- In a DFS forest with several roots, what does it mean for two vertex intervals to be disjoint? Can either vertex be an ancestor of the other?
- Why must a descendant's finish time be less than its ancestor's finish time in recursive DFS?
- Can a directed edge from \(q\) to \(x\) guarantee that \(q\) is an ancestor of \(x\)? Explain what the interval test establishes instead.