Find path in implicit Fibonacci tree
Company: Databricks
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Onsite
Quick Answer: This question evaluates understanding of recursion, tree traversal, implicit data structures, and index arithmetic for navigating preorder-numbered Fibonacci trees and computing node-to-node paths.
Constraints
- 1 <= k <= 60
- 1 <= a, b <= size(k)
- Do not build the full tree explicitly; use its recursive structure and subtree sizes
Examples
Input: (1, 1, 1)
Expected Output: [1]
Explanation: T(1) has only one node, so the path from 1 to 1 is just [1].
Input: (4, 3, 5)
Expected Output: [3, 2, 1, 5]
Explanation: In T(4), node 3 is under the left subtree and node 5 is the right child of the root, so the path goes 3 -> 2 -> 1 -> 5.
Input: (5, 4, 5)
Expected Output: [4, 3, 5]
Explanation: In T(5), nodes 4 and 5 are the two children of node 3, so the path is 4 -> 3 -> 5.
Input: (5, 1, 9)
Expected Output: [1, 7, 9]
Explanation: Node 1 is the root. Node 9 lies in the right subtree rooted at 7, so the path is 1 -> 7 -> 9.
Input: (6, 7, 14)
Expected Output: [7, 3, 2, 1, 11, 12, 14]
Explanation: Node 7 is in the left subtree of the root, while 14 is in the right subtree. Their lowest common ancestor is 1.
Input: (6, 10, 10)
Expected Output: [10]
Explanation: When both endpoints are the same node, the path contains only that node.
Input: (8, 1, 41)
Expected Output: [1, 27, 37, 41]
Explanation: In T(8), the path from the root to the last preorder index follows the right subtree roots 27 and 37, then reaches 41.
Solution
def solution(k, a, b):
max_k = max(2, k)
sizes = [0] * (max_k + 1)
sizes[1] = 1
sizes[2] = 1
for i in range(3, k + 1):
sizes[i] = 1 + sizes[i - 1] + sizes[i - 2]
def path_to_root(order, target):
root = 1
ancestors = []
while target != root:
ancestors.append(root)
left_size = sizes[order - 1]
left_root = root + 1
left_end = root + left_size
if left_root <= target <= left_end:
root = left_root
order -= 1
else:
root = root + 1 + left_size
order -= 2
return [target] + ancestors[::-1]
path_a = path_to_root(k, a)
path_b = path_to_root(k, b)
i = len(path_a) - 1
j = len(path_b) - 1
while i >= 0 and j >= 0 and path_a[i] == path_b[j]:
i -= 1
j -= 1
return path_a[:i + 2] + path_b[:j + 1][::-1]
Time complexity: O(k). Space complexity: O(k).
Hints
- For a subtree T(t) rooted at global index r, the left subtree root is at r + 1 and occupies indices [r + 1, r + size(t - 1)]. The right subtree starts immediately after that block.
- Compute the path from each target node up to the root using only index ranges. Then find the lowest common ancestor by comparing the two rootward paths from the end.