Tree Topics¶
Table of Contents
1. Traversals¶
DFS and Stack Based
Example Code
1def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: 2 def dfs(root): 3 nonlocal result 4 if not root: 5 return 6 result.append(root.val) 7 dfs(root.left) 8 dfs(root.right) 9 10 def iter(root): 11 nonlocal result 12 stack = [root] if root else [] 13 while stack: 14 # stack top == current call stack 15 node = stack.pop() 16 result.append(node.val) 17 18 """ remmeber to push right first """ 19 if node.right: 20 stack.append(node.right) 21 if node.left: 22 stack.append(node.left) 23 24def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: 25 def dfs(root): 26 nonlocal result 27 if not root: 28 return 29 dfs(root.left) 30 result.append(root.val) 31 dfs(root.right) 32 33 def push_left(stack, root): 34 while root: 35 stack.append(root) 36 root = root.left 37 38 def iter(root): 39 nonlocal result 40 """ 41 we need to simulate how the call stack works 42 (a) push as many left elements as possible as init 43 (b) popping means processing 44 (c) once we pop, we need to move right and then do (a) again 45 """ 46 stack = [] 47 """ NOTE we have to do this before entering the loop """ 48 push_left(stack, root) 49 50 while stack: 51 node = stack.pop() 52 result.append(node.val) 53 push_left(stack, node.right) 54 55 result = [] 56 iter(root) 57 return result 58 59# Definition for a binary tree node. 60# class TreeNode: 61# def __init__(self, val=0, left=None, right=None): 62# self.val = val 63# self.left = left 64# self.right = right 65class BSTIterator: 66 67 def __init__(self, root: Optional[TreeNode]): 68 self.stack = [] 69 self.push_left(root) 70 71 def push_left(self, node): 72 while node: 73 self.stack.append(node) 74 node = node.left 75 76 def next(self) -> int: 77 top = self.stack.pop() 78 self.push_left(top.right) 79 return top.val 80 81 def hasNext(self) -> bool: 82 return len(self.stack) > 0 83 84 85# Your BSTIterator object will be instantiated and called as such: 86# obj = BSTIterator(root) 87# param_1 = obj.next() 88# param_2 = obj.hasNext()
2. Lowest Common Ancestor¶
Tree LCA variants
Example Code
1# binary tree 2""" unique nodes, p, q guaranteed to exist in tree """ 3def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': 4 # three possible cases 5 # if root = p and any of the subtrees contain q, then p is the lca 6 # if root = q and any of the subtrees contain p, then q is the lca 7 # if one of the subtrees contains p and the other contains q, then root is the lca 8 def lca(root, p, q): 9 if not root: 10 return root 11 # if any of the nodes are found first, they are returned 12 # case 1: the other node is one of the subtrees - in this case, root is lca 13 # case 2: the other node is in another part, then it would help upstream 14 # level root to figure it out 15 if root == p or root == q: 16 return root 17 18 left = lca(root.left, p, q) 19 right = lca(root.right, p, q) 20 21 if left is not None and right is not None: 22 return root 23 if left is not None: 24 return left 25 if right is not None: 26 return right 27 return None 28 29 return lca(root, p, q) 30 31# binary search tree 32""" unique nodes, p, q guaranteed to exist in tree """ 33def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': 34 def lca(root, p, q): 35 # we can assume that p.val < q.val 36 if not root: 37 return root 38 # here we effectively prune the search space using bst property 39 if q.val < root.val: 40 return lca(root.left, p, q) 41 if root.val < p.val: 42 return lca(root.right, p, q) 43 # we can confidently return root if p.val < root.val < q.val 44 # since it is guaranteed that the nodes are going to be present 45 # in the binary search tree 46 return root 47 48 if p.val > q.val: 49 p, q = q, p 50 return lca(root, p, q) 51 52""" 53Older code 54# Binary tree structure 55class TreeNode: 56 def __init__(self, val=0, left=None, right=None): 57 self.val = val 58 self.left = left 59 self.right = right 60 61class Solution: 62 def lca1(self, root, p, q): 63 if root is None: 64 return root 65 if root == p or root == q: 66 return root 67 left = self.lca1(root.left, p, q) 68 right = self.lca1(root.right, p, q) 69 if left is not None and right is not None: 70 return root 71 return left if left is not None else right 72 def lca2(self, root, p, q): 73 if root is None: 74 return None 75 if p is None and q is None: 76 return None 77 if root == p: 78 if q is None: 79 return root 80 left = self.lca2(root.left, None, q) 81 right = self.lca2(root.right, None, q) 82 if left is not None or right is not None: 83 return root 84 elif root == q: 85 if p is None: 86 return root 87 left = self.lca2(root.left, p, None) 88 right = self.lca2(root.right, p, None) 89 if left is not None or right is not None: 90 return root 91 else: 92 left = self.lca2(root.left, p, q) 93 right = self.lca2(root.right, p, q) 94 if left is not None and right is not None: 95 return root 96 return None 97 98def test1(): 99 # Test Case 100 root = TreeNode(3) 101 root.left = TreeNode(5) 102 root.right = TreeNode(1) 103 root.left.left = TreeNode(6) 104 root.left.right = TreeNode(2) 105 root.right.left = TreeNode(0) 106 root.right.right = TreeNode(8) 107 root.left.right.left = TreeNode(7) 108 root.left.right.right = TreeNode(4) 109 110 # Nodes 111 p = root.left # Node with value 5 112 q = root.right # Node with value 1 113 114 # Expected Output: 3 115 return root, p, q 116 117def test2(): 118 # Binary tree structure 119 root = TreeNode(3) 120 root.left = TreeNode(5) 121 root.right = TreeNode(1) 122 root.left.left = TreeNode(6) 123 root.left.right = TreeNode(2) 124 125 # Nodes 126 p = root.left # Node with value 5 127 q = TreeNode(10) # Node with value 10 (not present in the tree) 128 129 # Expected Output: None (as `q` is not in the tree) 130 return root, p, q 131 132def test3(): 133 # Binary tree structure 134 root = TreeNode(3) 135 root.left = TreeNode(5) 136 root.right = TreeNode(5) 137 root.left.left = TreeNode(6) 138 root.left.right = TreeNode(2) 139 140 # Nodes 141 p = root.left # Node with value 5 (left subtree) 142 q = root.right # Node with value 5 (right subtree) 143 144 # Expected Output: 3 145 return root, p, q 146 147if __name__ == '__main__': 148 solution = Solution() 149 root, p, q = test1() 150 res = solution.lca2(root, p, q) 151 print(res.val) 152"""
3. Validation¶
Is valid BST
Example Code
1def isValidBST(self, root: Optional[TreeNode]) -> bool: 2 def range(root, tree_min, tree_max) -> bool: 3 if not root: 4 return True 5 6 if tree_min < root.val and root.val < tree_max: 7 # root is valid, now check subtrees 8 left_valid = range(root.left, tree_min, root.val) 9 right_valid = range(root.right, root.val, tree_max) 10 if left_valid and right_valid: 11 return True 12 13 return False 14 15 def minmax(root) -> Tuple[int,int]: 16 # returns min, max of the tree rooted at root 17 if not root: 18 # note that the order is kept like this 19 # so that any parent subtree is not invalidated because 20 # of empty children 21 return inf, -inf 22 23 left_min, left_max = minmax(root.left) 24 right_min, right_max = minmax(root.right) 25 26 if left_max < root.val and root.val < right_min: 27 left_min = min(left_min, root.val) 28 right_max = max(right_max, root.val) 29 return left_min, right_max 30 31 # if comes here, then all parent subtrees are invalidated 32 return -inf, inf 33 34 # tree_min, tree_max = minmax(root) 35 # return tree_min > -inf and tree_max < inf 36 37 return range(root, -inf, inf)
Is valid preorder
Example Code
1def verifyPreorder(self, preorder: List[int]) -> bool: 2 # the idea is to use a monotonic stack 3 # to simulate call stack in a bst 4 """ 5 IMPORTANT 6 BST assumption for preorder is that once a node is processed 7 all the left subtree nodes are also processed. So the only values 8 we're allowed to process are larger than the node. 9 """ 10 stack = [] 11 # all next values should be larger than this 12 min_limit = -inf 13 for curr in preorder: 14 while stack and stack[-1] < curr: 15 # updating the min_limit here because nothing that 16 # comes after should be smaller than this 17 min_limit = stack.pop() 18 if min_limit > curr: 19 return False 20 stack.append(curr) 21 return True
Serialize and Deserialize BST
Example Code
1# Definition for a binary tree node. 2# class TreeNode: 3# def __init__(self, x): 4# self.val = x 5# self.left = None 6# self.right = None 7 8class Codec: 9 10 def serialize(self, root: Optional[TreeNode]) -> str: 11 """Encodes a tree to a single string. 12 """ 13 # note that just the preorder traversal is sufficient for serialization 14 preorder = self.iterative_preorder_traversal(root) 15 return ','.join([str(x) for x in preorder]) 16 17 def deserialize(self, data: str) -> Optional[TreeNode]: 18 """Decodes your encoded data to tree. 19 """ 20 if len(data) == 0: 21 return None 22 23 preorder = [int(x) for x in data.split(',')] 24 return self.construct_from_preorder(preorder) 25 26 def construct_from_preorder(self, preorder: List[int]) -> Optional[TreeNode]: 27 assert(len(preorder) > 0) 28 29 # root needs special attention as it has to be returned 30 root = TreeNode(preorder[0]) 31 max_stack = [root] 32 33 for index in range(1, len(preorder)): 34 node = TreeNode(preorder[index]) 35 36 last_popped = None 37 while max_stack and max_stack[-1].val < node.val: 38 last_popped = max_stack.pop() 39 40 if last_popped is not None: 41 last_popped.right = node 42 elif max_stack: 43 max_stack[-1].left = node 44 45 max_stack.append(node) 46 47 return root 48 49 def iterative_preorder_traversal(self, root: Optional[TreeNode]) -> List[int]: 50 preorder = [] 51 52 stack = [root] if root else [] 53 while stack: 54 node = stack.pop() 55 preorder.append(node.val) 56 if node.right: 57 stack.append(node.right) 58 if node.left: 59 stack.append(node.left) 60 61 return preorder 62 63# Your Codec object will be instantiated and called as such: 64# Your Codec object will be instantiated and called as such: 65# ser = Codec() 66# deser = Codec() 67# tree = ser.serialize(root) 68# ans = deser.deserialize(tree) 69# return ans
3. Segment Tree¶
Basics - Sum queries - Update queries - Memory efficient implementation
Advanced Topics - Finding the maximum - Finding the maximum and the number of times it appears - Compute the greatest common divisor / least common multiple - Counting the number of zeros, searching for the $k$ -th zero - Searching for an array prefix with a given amount - Searching for the first element greater than a given amount - Finding subsegments with the maximal sum
Saving the entire subarrays in each vertex - Find the smallest number greater or equal to a specified number. No modification queries. - Find the smallest number greater or equal to a specified number. With modification queries. - Find the smallest number greater or equal to a specified number. Acceleration with “fractional cascading”.
Range updates (Lazy Propagation) - Addition on segments - Assignment on segments - Adding on segments, querying for maximum
Generalization to higher dimensions - Simple 2D Segment Tree - Compression of 2D Segment Tree
Preserving the history of its values (Persistent Segment Tree) - Finding the $k$ -th smallest number in a range
Dynamic segment tree
4. Misc¶
[stack] https://leetcode.com/problems/binary-tree-preorder-traversal/
https://leetcode.com/problems/verify-preorder-sequence-in-binary-search-tree/description/
[stack] https://leetcode.com/problems/binary-tree-inorder-traversal/description/
[stack] https://leetcode.com/problems/binary-tree-postorder-traversal/
https://leetcode.com/problems/reverse-odd-levels-of-binary-tree/description/
https://leetcode.com/problems/subtree-of-another-tree/description/
https://leetcode.com/problems/count-univalue-subtrees/description/
https://leetcode.com/problems/balance-a-binary-search-tree/description/
https://leetcode.com/problems/flatten-binary-tree-to-linked-list/description/
https://leetcode.com/problems/correct-a-binary-tree/description/
https://leetcode.com/problems/serialize-and-deserialize-bst/
https://leetcode.com/problems/serialize-and-deserialize-binary-tree/
https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/
https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/
https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-ii/description/
https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii/description/
https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iv/description/