Given a binary tree and two tree pointers, x and y, write an efficient algorithm to check if they lie on the same root-to-leaf path in the binary tree. In other words, determine whether x is an ancestor of y, or x is a descendant of y. The algorithm should work in minimal time for numerous inputs.

For example, consider the following binary tree. The nodes 2 and 8 lie on the same root-to-leaf path, while nodes 5 and 9 do not lie on the same root-to-leaf path.

Binary Tree

Practice this problem

Prerequisite:

Arrival and departure time of vertices in DFS

 
A simple solution would be to traverse the binary tree and check if the node x lies in either the left or right subtree of y, or vice versa. However, if m lookup calls are made to the binary tree having n nodes, then this solution takes O(n.m) time. The following solution reduces the time complexity to O(m + n).

 
The idea is to find the arrival and departure time of each tree node by doing a Depth–first search (DFS) on the binary tree. For a binary tree, the DFS algorithm is a simple preorder or postorder traversal. The arrival time is the time when the DFS first explores the node and the departure time is the time at which all children of the node are visited, and we are ready to backtrack.

Therefore, before exploring the left and right child of any node in DFS, mark the arrival time of that node, and after exploring the left and right child of the node, mark its departure time. For example, here are each tree node’s arrival and departure time on traversing the above binary tree in a preorder fashion.

Node 1 —> (1, 28)
Node 2 —> (2, 17)
Node 4 —> (3, 12)
Node 8 —> (4, 5)
Node 9 —> (6, 11)
Node 12 —> (7, 8)
Node 13 —> (9, 10)
Node 5 —> (13, 16)
Node 10 —> (14, 15)
Node 3 —> (18, 27)
Node 6 —> (19, 20)
Node 7 —> (21, 26)
Node 11 —> (22, 25)
Node 14 —> (23, 24)

 
Important Observations:

If x is an ancestor of y, the relation between the arrival and departure time is:

arrival[x] < arrival[y] < departure[y] < departure[x]

Alternatively, if x is a descendant of y, the relation between the arrival and departure time is:

arrival[y] < arrival[x] < departure[x] < departure[y]

The algorithm can be implemented as follows in C++, Java, and Python:

C++


Download  Run Code

Java


Download  Run Code

Python


Download  Run Code

Output:
 
Node 3 is an ancestor of Node 14
Node 12 is a direct descendant of Node 2
Node 4 is a neither an ancestor nor a descendant of Node 5
Node 4 or Node 5 is not the actual node in the tree

The time complexity of the above solution is O(n + m), where n is the total number of nodes in the binary tree and m is the total number of lookups. We can do the constant-time lookups any number of times once the binary tree is preprocessed.