Tutorials › Graph Theory (Undergraduate) › The Parenthesis Theorem for DFS Intervals

Graph Traversal: BFS and DFS · Tutorial 723 of 799

The Parenthesis Theorem for DFS Intervals

Use DFS timestamps to recognize nested subtrees and determine ancestry, including when the search produces several trees.

Intermediate 11 min read

What You'll Learn

  • Define discovery-finish intervals for recursive DFS
  • Prove that two DFS intervals are nested or disjoint
  • Relate strict interval containment to ancestry in a DFS forest
  • Trace timestamps with a specified vertex and adjacency order
  • Recognize why overlapping, non-nested intervals signal an invalid trace

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.

Definition: In recursive DFS, when a vertex \(v\) is first visited, assign it a discovery time \(d(v)\). After every outgoing edge of \(v\) has been examined and all recursive calls from \(v\) have returned, assign it a finish time \(f(v)\). A vertex's DFS interval is the closed interval \([d(v),f(v)]\). The DFS forest consists of the tree edges by which vertices are first discovered, with one rooted tree for each DFS root.

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

Theorem (Parenthesis Theorem): For any two distinct vertices \(u\) and \(v\) in a DFS forest, exactly one of the following holds:
  • 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)\).
Thus two DFS intervals cannot overlap without one containing the other. The discovery and finish events can be written as a properly nested sequence of opening and closing parentheses: discovery opens an interval, and finishing closes it.

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.

TimeEvent
1Discover \(r\)
2Discover \(a\)
3Discover \(b\)
4Discover \(c\)
5Finish \(c\)
6Finish \(b\)
7Finish \(a\)
8Discover \(d\)
9Discover \(e\)
10Finish \(e\)
11Finish \(d\)
12Finish \(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.

Theorem (Ancestor–Interval Characterization): If \(u\) and \(v\) are distinct vertices in a DFS forest, then \(u\) is an ancestor of \(v\) if and only if \[ d(u)<d(v)<f(v)<f(u). \] Equivalently, the interval of \(v\) is strictly contained in the interval of \(u\).

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\).

TimeEvent
1Discover \(1\)
2Discover \(2\)
3Finish \(2\)
4Finish \(1\)
5Discover \(3\)
6Discover \(4\)
7Finish \(4\)
8Finish \(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:

TimeEvent
1Discover \(s\)
2Discover \(p\)
3Discover \(x\)
4Finish \(x\)
5Finish \(p\)
6Discover \(q\)
7Finish \(q\)
8Finish \(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.

  1. 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?
  2. Can two distinct vertices in a valid DFS forest have intervals \([2,8]\) and \([6,11]\)? Explain using the Parenthesis Theorem.
  3. 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?
  4. Why must a descendant's finish time be less than its ancestor's finish time in recursive DFS?
  5. Can a directed edge from \(q\) to \(x\) guarantee that \(q\) is an ancestor of \(x\)? Explain what the interval test establishes instead.