Check if 2 tree nodes are related ancestor/descendant in O1 with pre-processing
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Yes, you can answer ancestor and descendant queries in O(1) time after linear preprocessing. The standard method is to run a depth-first traversal once, record entry and exit times for every node, and then compare intervals.
The Preprocessing Idea
During DFS, give each node two timestamps:
- '
tin[node]when you first enter the node' - '
tout[node]when you finish exploring its subtree'
These timestamps create a nesting property:
node u is an ancestor of node v if and only if:
- '
tin[u] <= tin[v]' - '
tout[u] >= tout[v]'
That works because every node in a subtree is visited completely between the ancestor's entry and exit times.
Python Example
Output:
The preprocessing takes O(n) time for a tree with n nodes, and each query afterward is constant time.
Why It Works
DFS enters a node before any node in its subtree and exits it after every node in its subtree is done. So each subtree becomes an interval, and subtrees are either nested or disjoint.
That turns ancestry into a simple interval-containment test.
This is much cleaner than walking parent pointers on every query, which would take up to O(h) time where h is the tree height.
Extending the Technique
The same timestamps are useful for more than ancestry checks. They often appear in:
- subtree queries
- Euler tour array techniques
- lowest common ancestor preprocessing
- flattening trees for segment trees or Fenwick trees
So even if you only need ancestor checks today, the preprocessing is a strong general-purpose foundation.
Handling Parent Pointers and Root Choice
You need a rooted tree for the ancestor relation to make sense. In an undirected tree, choose a root first and make sure DFS does not walk back to the parent as if it were a child.
If your input stores parent pointers already, preprocessing is still worth it when you need many queries. One-time DFS gives you O(1) queries instead of repeated upward walks.
For example, a single query might be cheap enough with parent pointers alone, but thousands or millions of ancestry checks make the timestamp method much more attractive. That is why this trick appears so often in competitive programming and tree-heavy systems code.
Common Pitfalls
- Forgetting to root the tree before talking about ancestors.
- Using only entry time without exit time.
- Reusing the technique on general graphs without preventing revisits.
- Assuming this handles dynamic tree edits automatically. If the tree changes, the timestamps may need recomputation.
- Mixing strict and non-strict comparisons inconsistently.
Summary
- Preprocess the tree once with DFS and record entry and exit times.
- Node
uis an ancestor of nodevwhenu's interval containsv's interval. - Preprocessing is
O(n)and each query isO(1). - The method works on rooted trees and extends naturally to many other tree algorithms.
- For many ancestor queries, this is one of the simplest and most effective techniques available.
Related reading
- Check if a binary tree is a mirror image or symmetric
- Check if a list is a rotation of another list that works with duplicates
- Check if a permutation of a string can become a palindrome
- Check if a string is rotation of another WITHOUT concatenating
- Check if a Bash array contains a value
- Check if a given key already exists in a dictionary
- Check TPU workload/utilization
- Checking Kubernetes pod CPU and memory utilization

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.