|
| 1 | +""" |
| 2 | +105. Construct Binary Tree from Preorder and Inorder Traversal |
| 3 | +
|
| 4 | +https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal |
| 5 | +
|
| 6 | +NOTES |
| 7 | + * Good primer for 'Serialize and Deserialize Binary Tree'. Recall that, |
| 8 | +
|
| 9 | + >No single traversal order (pre-order, post-order, or in-order) uniquely |
| 10 | + identifies the structure of a tree... |
| 11 | +
|
| 12 | +If the binary tree was guaranteed to be complete, meaning all levels except |
| 13 | +possibly the last are fully filled and the last level is filled from left to |
| 14 | +right, this problem would be trivial. Recall that a heap, which is a complete |
| 15 | +binary tree, can be represented as a zero-based array where a node at position |
| 16 | +i has children at positions 2*i+1 and 2*i+2. So, all we would need to do is |
| 17 | +construct the tree by iterating through the pre-order array. Unfortunately, |
| 18 | +it's not that simple, since the tree is *not* guaranteed to be a complete tree |
| 19 | +and therefore we must account for gaps. |
| 20 | +
|
| 21 | +Using the definitions of pre-order and in-order traversal, we can make two key |
| 22 | +observations: |
| 23 | +
|
| 24 | + 1. The root of the tree is located at `preorder[0]`. |
| 25 | + 2. The subarrays created by splitting `inorder` at root represents the left |
| 26 | + and right subtrees of the root of the tree. |
| 27 | +
|
| 28 | +From this, a recursive logic emerges: the subtrees rooted at `preorder[i]` are |
| 29 | +created by splitting `inorder` at `preorder[i]`. |
| 30 | +
|
| 31 | +It should be noted that the range for the subtrees is reduced logarithmically |
| 32 | +for each recursive call. |
| 33 | +
|
| 34 | +One of the major "gotchas" in this problem is that we cannot assume that a node |
| 35 | +in `preorder` is the left or right node. For this reason, we only increment the |
| 36 | +pointer to the index of `preorder` if a node can be created (i.e., the |
| 37 | +recursive function does not return None). |
| 38 | +
|
| 39 | +For example, consider the following tree: |
| 40 | +
|
| 41 | + 1 |
| 42 | + ● |
| 43 | + \ |
| 44 | + 2 |
| 45 | + ● |
| 46 | +
|
| 47 | +Pre-order = [1, 2] |
| 48 | +In-order = [1, 2] |
| 49 | +
|
| 50 | +The only way to signify programmatically that 2 is the right node of 1 and not |
| 51 | +the left node is to only increment the index of `preorder` if the result of |
| 52 | +calling our recursive function does not return None. |
| 53 | +""" |
| 54 | + |
| 55 | +from src.classes import TreeNode |
| 56 | + |
| 57 | + |
| 58 | +class Solution: |
| 59 | + def buildTree(self, preorder: list[int], inorder: list[int]) -> TreeNode | None: |
| 60 | + # Create a mapping of value -> index for `inorder` (O(1) lookups). |
| 61 | + d: dict[int, int] = {} |
| 62 | + for i, v in enumerate(inorder): |
| 63 | + d[v] = i |
| 64 | + |
| 65 | + preorder_idx = 0 |
| 66 | + |
| 67 | + def _buildTree(left: int, right: int) -> TreeNode | None: |
| 68 | + """ |
| 69 | + Helper function for `buildTree()`. |
| 70 | + """ |
| 71 | + nonlocal preorder_idx |
| 72 | + |
| 73 | + if left > right: |
| 74 | + return None |
| 75 | + |
| 76 | + preorder_val = preorder[preorder_idx] |
| 77 | + node = TreeNode(preorder_val) |
| 78 | + |
| 79 | + preorder_idx += 1 |
| 80 | + |
| 81 | + node.left = _buildTree(left, d[preorder_val] - 1) |
| 82 | + node.right = _buildTree(d[preorder_val] + 1, right) |
| 83 | + |
| 84 | + return node |
| 85 | + |
| 86 | + return _buildTree(0, len(preorder) - 1) |
0 commit comments