Check inorder subsequence and edit tree minimally
Company: Airbnb
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates proficiency in tree traversal and subsequence matching under tight resource constraints, testing skills in streaming inorder traversal, memory-efficient algorithms, and reasoning about subsequences within binary trees; it falls under the Coding & Algorithms domain and requires practical implementation ability combined with conceptual algorithmic reasoning. The follow-up on minimally editing the tree and computing a minimum-cost edit sequence assesses understanding of edit-distance-like reasoning on tree structures, handling edge cases (empty target, duplicates, longer target) and analyzing time/space complexity, which is commonly asked to evaluate optimization, problem decomposition, and correctness under constraints.
Part 1: Check Whether Target Is an Inorder Subsequence
Constraints
- 0 <= number of nodes <= 100000
- 0 <= len(target) <= 100000
- -10^9 <= node values, target values <= 10^9
- Duplicate values are allowed
Examples
Input: ({'val': 2, 'left': {'val': 1, 'left': None, 'right': None}, 'right': {'val': 3, 'left': None, 'right': None}}, [1, 3])
Expected Output: True
Explanation: The inorder traversal is [1, 2, 3], and [1, 3] is a subsequence.
Input: ({'val': 2, 'left': {'val': 1, 'left': None, 'right': None}, 'right': {'val': 3, 'left': None, 'right': None}}, [3, 1])
Expected Output: False
Explanation: The values appear in the wrong relative order.
Input: ({'val': 2, 'left': {'val': 1, 'left': None, 'right': None}, 'right': {'val': 2, 'left': None, 'right': None}}, [2, 2])
Expected Output: True
Explanation: The inorder traversal is [1, 2, 2], so the duplicate target is present as a subsequence.
Input: (None, [])
Expected Output: True
Explanation: The empty array is a subsequence of any traversal, including an empty one.
Input: (None, [1])
Expected Output: False
Explanation: A non-empty target cannot be a subsequence of an empty traversal.
Hints
- Keep one pointer into target. Advance it only when the current inorder value matches target[pointer].
- Morris inorder traversal lets you stream inorder values using O(1) auxiliary space by temporarily threading the tree.
Part 2: Deterministically Modify the Tree So Target Appears in Inorder
Constraints
- 0 <= number of nodes <= 100000
- 0 <= len(target) <= 100000
- -10^9 <= node values, target values <= 10^9
- Duplicate values are allowed
Examples
Input: ({'val': 2, 'left': {'val': 1, 'left': None, 'right': None}, 'right': {'val': 3, 'left': None, 'right': None}}, [4, 5])
Expected Output: {'val': 5, 'left': {'val': 4, 'left': None, 'right': None}, 'right': {'val': 3, 'left': None, 'right': None}}
Explanation: The first two inorder nodes are overwritten with 4 and 5. The last node stays unchanged.
Input: ({'val': 7, 'left': None, 'right': None}, [1, 2, 3])
Expected Output: {'val': 1, 'left': None, 'right': {'val': 2, 'left': None, 'right': {'val': 3, 'left': None, 'right': None}}}
Explanation: Overwrite the only existing node with 1, then append 2 and 3 as a right chain.
Input: ({'val': 1, 'left': None, 'right': None}, [])
Expected Output: {'val': 1, 'left': None, 'right': None}
Explanation: An empty target requires no change.
Input: (None, [9, 8])
Expected Output: {'val': 9, 'left': None, 'right': {'val': 8, 'left': None, 'right': None}}
Explanation: An empty tree becomes a pure right-child chain containing the target values.
Hints
- If you overwrite the first visited inorder nodes with target values in order, target becomes a prefix of the modified inorder traversal.
- Any remaining target values can be appended at the end of inorder by attaching a right-child chain to the original rightmost node.
Part 3: Minimum-Cost Edit Plan for Inorder Subsequence
Constraints
- 0 <= number of nodes <= 200
- 0 <= len(target) <= 200
- -10^9 <= node values, target values <= 10^9
- Duplicate values are allowed
Examples
Input: ({'val': 2, 'left': {'val': 1, 'left': None, 'right': None}, 'right': {'val': 3, 'left': None, 'right': None}}, [1, 3])
Expected Output: (0, [])
Explanation: The target is already a subsequence of the inorder traversal [1, 2, 3].
Input: ({'val': 2, 'left': {'val': 1, 'left': None, 'right': None}, 'right': {'val': 3, 'left': None, 'right': None}}, [1, 4, 3])
Expected Output: (1, [('overwrite', 1, 4)])
Explanation: Using the deterministic tie-break, overwrite original inorder index 1 (value 2) to 4.
Input: ({'val': 2, 'left': {'val': 1, 'left': None, 'right': None}, 'right': {'val': 3, 'left': None, 'right': None}}, [3, 1])
Expected Output: (1, [('insert_before', 0, 3)])
Explanation: Insert 3 before original inorder index 0, then use the original 1.
Input: ({'val': 5, 'left': None, 'right': None}, [1])
Expected Output: (1, [('overwrite', 0, 1)])
Explanation: Overwrite the only original node.
Input: (None, [2, 2])
Expected Output: (2, [('insert_before', 0, 2), ('insert_before', 0, 2)])
Explanation: With no original nodes, both target values must be inserted.
Hints
- After converting the tree to its inorder sequence, the problem becomes sequence DP.
- At DP state (i, j), consider three choices: skip inorder[i], use inorder[i] for target[j] with cost 0 or 1, or insert target[j] with cost 1.