: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')
Example #2
0

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))
Example #5
0

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))
Example #6
0
    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')