:Time: O(N) :Space: O(N) :return: bottom left value in last row """ bleft = None # Track the bottom left node; just use the last node removed in bfs when looking at a new level q, curr_level = [(root, 1)], -1 while q: rm_node, rm_level = q.pop(0) if rm_level != curr_level: # at start of new level, update bottom left var bleft = rm_node curr_level = rm_level # set current level to the new one if rm_node.left: # Add children q.append((rm_node.left, rm_level + 1)) if rm_node.right: q.append((rm_node.right, rm_level + 1)) return bleft.key if __name__ == '__main__': test = TreeNode(1) test.left = TreeNode(2) test.right = TreeNode(3) test.left.left = TreeNode(4) test.right.left = TreeNode(5) test.right.right = TreeNode(6) test.right.left.left = TreeNode(7) pretty_print_tree(test) assert find_bottom_left_value(test) == 7 print('PASSED')
def get_max_diff(root, curr): if not root.left and not root.right: # BC with Leaf Node return abs(curr - root.key) this = abs(curr - root.key) # Calculate 'V' value for this current node # Include/Exclude incl_left = incl_right = excl_left = excl_right = float('-inf') if root.left: incl_left, excl_left = get_max_diff(root.left, root.key), get_max_diff( root.left, curr) if root.right: incl_right, excl_right = get_max_diff(root.right, root.key), get_max_diff( root.right, curr) return max(max(incl_right, incl_left), max(excl_right, excl_left), this) if __name__ == '__main__': t = TreeNode(8) t.left = TreeNode(3) t.right = TreeNode(10) t.left.left = TreeNode(1) t.left.right = TreeNode(6) t.right.right = TreeNode(14) t.left.right.left = TreeNode(4) t.left.right.right = TreeNode(7) t.right.right.left = TreeNode(13) pretty_print_tree(t) print(maxAncestorDiff(t))
# Binary midpoint insertion routine def insert(A: List[int], lo: int, hi: int) -> TreeNode: """ Inserts elements and returns root of new tree. @Algorithm: Cannot insert in any sorted, serial order. Must insert in a "shuffled" manner. How? - Well, think of mid of array as pivot (root). Left of this pivot must be in the left subtree and the right of the pivot must be in the right subtree. - Now... we know the left of the pivot must go on the left. But, again, we cannot process the elements in a serial fashion; therefore, like above, we recursively insert left/right based on the midpoint approach. :param A: list of numbers :param lo: lower endpoint interval :param hi: higher endpoint interval :Time: O(N) :Space: O(N) :return: root of resulting tree """ if lo > hi: return None mid = (lo + hi) // 2 root = TreeNode(A[mid]) root.left, root.right = insert(A, lo, mid - 1), insert(A, mid + 1, hi) return root if __name__ == '__main__': test1 = [-10, -3, 0, 5, 9] test2 = [-6, -2, -1, 1, 6, 21, 22] pretty_print_tree(sortedArrayToBST(test1))
Algorithm: Essentially we need to track whether the current node, A[i], is allowable in the current tree by using min/max intervals. If it is outside those bounds we can't include it in our tree. If within bounds, since this is preorder traversal, we first recursively build the left subtree. However, we soon realize the need for a self.count variable that will allow us to ascertain at which point in the array we start to build our right subtree. Therefore we also track total nodes inserted. Hence, by this logic, we recurisively build the left subtree along with tracking the total nodes that have been inserted. Then, we know that the next element to look at to recursively build the right subtree will be at the position self.count (now updated to the count of nodes in left subtree) :param A: preorder output :param i: element being considered :param min: minimum interval for element to be included :param max: maximum interval for element to be included :Time: O(N) :Space: O(N) :return: """ # BC 1, 2: out-of-bounds of array and or current value being considered outside of min/max interval if i >= len(A) or A[i] > max or A[i] < min: return None root, self.count = TreeNode(A[i]), self.count + 1 # Create root and increment total nodes seen root.left = self.construct(A, i + 1, min, root.key) # Recursively build and assign left subtree root.right = self.construct(A, self.count, root.key, max) # Recurse to pos total nodes seen return root if __name__ == '__main__': A = [8, 5, 1, 7, 10, 12] # A = [1, 2, 3] pretty_print_tree(Solution().bstFromPreorder(A))
def prune_tree(root: TreeNode) -> TreeNode: """ Returns a pruned tree. Essentially removing all leaf nodes with value 0. :param root: root of tree :Time: O(N) :Space: O(N) :return: root of resulting tree """ if not root: # BC: If empty tree nothing to prune return None root.left, root.right = prune_tree(root.left), prune_tree( root.right) # prune left and right if root.key == 0 and (not root.left and not root.right): return None # If root is a leaf node with value 0, remove it else: return root if __name__ == '__main__': t = TreeNode(1) t.left = TreeNode(0) t.right = TreeNode(1) t.left.left = TreeNode(0) t.left.right = TreeNode(0) t.right.left = TreeNode(0) t.right.right = TreeNode(1) pretty_print_tree(t) pretty_print_tree(prune_tree(t))
return min_tree_heights(min_depth(root.left), min_depth(root.right)) + 1 def min_tree_heights(t1_h: int, t2_h: int) -> int: """ Given heights of two trees, returns the minimum height, but where an empty tree is considered invalid for this use case. :param t1_h: height of tree 1 :param t2_h: height of tree 2 :return: minimum of the tree heights """ if not t1_h and not t2_h: return 0 elif not t1_h: return t2_h elif not t2_h: return t1_h else: return min(t1_h, t2_h) if __name__ == '__main__': t1 = TreeNode(3) t1.left = TreeNode(9) t1.right = TreeNode(20) t1.right.left = TreeNode(15) t1.right.right = TreeNode(7) pretty_print_tree(t1) print(min_depth(t1)) assert min_depth(t1) == 2, 'ERROR' print('PASSED')