Example #1
0
from typing import Optional

from leetcode import TreeNode, new_tree


def is_valid_bst(root: TreeNode) -> bool:
    def is_valid_bst_bounded(node: TreeNode, lower: Optional[int],
                             higher: Optional[int]) -> bool:
        if not node:
            return True
        if lower is not None and node.val <= lower:
            return False
        if higher is not None and node.val >= higher:
            return False
        left_is_valid = not node.left or is_valid_bst_bounded(
            node.left, lower, node.val)
        right_is_valid = not node.right or is_valid_bst_bounded(
            node.right, node.val, higher)
        return left_is_valid and right_is_valid

    return is_valid_bst_bounded(root, None, None)


if __name__ == "__main__":
    assert is_valid_bst(new_tree(2, 1, 3)) is True
    assert is_valid_bst(new_tree(5, 1, 4, None, None, 3, 6)) is False
    assert is_valid_bst(new_tree(10, 5, 15, None, None, 6, 20)) is False
    assert is_valid_bst(new_tree(0, None, -1)) is False
from leetcode import TreeNode, new_tree


def is_balanced(root: TreeNode) -> bool:
    if not root:
        return True
    if not (is_balanced(root.left) and is_balanced(root.right)):
        return False
    left_height = root.left.val if root.left else 0
    right_height = root.right.val if root.right else 0
    root.val = max(left_height, right_height) + 1
    return -1 <= left_height - right_height <= 1


if __name__ == "__main__":
    assert is_balanced(new_tree(3, 9, 20, None, None, 15, 7)) is True
    assert is_balanced(new_tree(1, 2, 2, 3, 3, None, None, 4, 4)) is False
from leetcode import TreeNode, test, new_tree


def count_nodes(root: TreeNode) -> int:
    if root is None:
        return 0

    queue, result = deque((root, )), 0
    while True:
        length = len(queue)
        result += length
        if queue[0].left is None:
            return result

        for _ in range(length):
            node = queue.popleft()
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)


test(
    count_nodes,
    [
        (new_tree(1, 2, 3, 4, 5, 6), 6),
        (None, 0),
        (new_tree(1), 1),
    ],
)
Example #4
0
        return

    prev = TreeNode(-(2 ** 31) - 1)
    first_error, second_error = None, None

    def inorder(node: TreeNode) -> None:
        nonlocal prev, first_error, second_error
        if node.left:
            inorder(node.left)
        if prev.val >= node.val and not first_error:
            first_error = prev
        if prev.val >= node.val and first_error:
            second_error = node
        prev = node
        if node.right:
            inorder(node.right)

    inorder(root)
    first_error.val, second_error.val = second_error.val, first_error.val


if __name__ == "__main__":
    tests = [
        (new_tree(1, 3, None, None, 2), new_tree(3, 1, None, None, 2)),
        (new_tree(3, 1, 4, None, None, 2), new_tree(2, 1, 4, None, None, 3)),
        (new_tree(5, 3, 9, -2147483648, 2), new_tree(5, 2, 9, -2147483648, 3)),
    ]
    for root, want in tests:
        recover_tree(root)
        assert root == want
Example #5
0
from leetcode import TreeNode, new_tree


def is_same_tree(lhs: TreeNode, rhs: TreeNode) -> bool:
    if lhs is None:
        return rhs is None
    elif rhs is None:
        return False
    else:
        return (
            lhs.val == rhs.val
            and is_same_tree(lhs.left, rhs.left)
            and is_same_tree(lhs.right, rhs.right)
        )


if __name__ == "__main__":
    assert is_same_tree(new_tree(1, 2, 3), new_tree(1, 2, 3)) is True
    assert is_same_tree(new_tree(1, None, 2), new_tree(1, 2)) is False
    assert is_same_tree(new_tree(1, 2, 1), new_tree(1, 1, 2)) is False
Example #6
0
from leetcode import TreeNode, test, new_tree


def is_balanced(root: TreeNode) -> bool:
    if not root:
        return True
    if not (is_balanced(root.left) and is_balanced(root.right)):
        return False
    left_height = root.left.val if root.left else 0
    right_height = root.right.val if root.right else 0
    root.val = max(left_height, right_height) + 1
    return -1 <= left_height - right_height <= 1


test(
    is_balanced,
    [
        (new_tree(3, 9, 20, None, None, 15, 7), True),
        (new_tree(1, 2, 2, 3, 3, None, None, 4, 4), False),
    ],
)
Example #7
0
        return False

    def walk_tree(root: TreeNode):
        left, right = root.left, root.right
        if left is None and right is None:
            yield root.val
        else:
            if left is not None:
                yield from walk_tree(left)
            if right is not None:
                yield from walk_tree(right)

    for v1, v2 in zip_longest(walk_tree(root1),
                              walk_tree(root2),
                              fillvalue=float("NaN")):
        if v1 != v2:
            return False
    return True


if __name__ == "__main__":
    assert (leaf_similar(
        new_tree(3, 5, 1, 6, 2, 9, 8, None, None, 7, 4),
        new_tree(3, 5, 1, 6, 7, 4, 2, None, None, None, None, None, None, 9,
                 8),
    ) is True)
    assert leaf_similar(new_tree(1), new_tree(1)) is True
    assert leaf_similar(new_tree(1), new_tree(2)) is False
    assert leaf_similar(new_tree(1, 2), new_tree(2, 2)) is True
    assert leaf_similar(new_tree(1, 2, 3), new_tree(1, 3, 2)) is False
Example #8
0
from leetcode import TreeNode, test, new_tree


def is_same_tree(lhs: TreeNode, rhs: TreeNode) -> bool:
    if lhs is None:
        return rhs is None
    elif rhs is None:
        return False
    else:
        return (lhs.val == rhs.val and is_same_tree(lhs.left, rhs.left)
                and is_same_tree(lhs.right, rhs.right))


test(
    is_same_tree,
    [
        (new_tree(1, 2, 3), new_tree(1, 2, 3), True),
        (new_tree(1, None, 2), new_tree(1, 2), False),
        (new_tree(1, 2, 1), new_tree(1, 1, 2), False),
    ],
)
    result, values, prev_value, count, max_count = [], [], 0, 0, 0

    def update() -> None:
        nonlocal prev_value, count, max_count
        max_count = max(max_count, count)
        values.append((prev_value, count))

    def inorder(node: TreeNode) -> None:
        nonlocal prev_value, count, max_count
        if not node:
            return
        inorder(node.left)
        if node.val == prev_value:
            count += 1
        else:
            update()
            prev_value, count = node.val, 1
        inorder(node.right)

    inorder(root)
    update()
    for value, count in values:
        if count > 0 and count == max_count:
            result.append(value)
    return result


if __name__ == "__main__":
    assert find_mode(new_tree(1, None, 2, 2)) == [2]
    assert find_mode(new_tree(0)) == [0]
Example #10
0
from leetcode import TreeNode, test, new_tree


def min_camara_cover(root: TreeNode) -> int:
    result = 0

    def lrd(node: TreeNode) -> int:
        nonlocal result
        if not node:
            return 1
        left, right = lrd(node.left), lrd(node.right)
        if left == 0 or right == 0:
            result += 1
            return 2
        if left == 1 and right == 1:
            return 0
        return 1

    if lrd(root) == 0:
        result += 1
    return result


test(
    min_camara_cover,
    [
        (new_tree(0, 0, None, 0, 0), 1),
        (new_tree(0, 0, None, 0, None, 0, None, None, 0), 2),
    ],
)
Example #11
0
from leetcode import TreeNode, new_tree


def invert_tree(root: TreeNode) -> TreeNode:
    def invert(node: TreeNode) -> None:
        node.left, node.right = node.right, node.left
        if node.left:
            invert(node.left)
        if node.right:
            invert(node.right)

    if root:
        invert(root)
    return root


if __name__ == "__main__":
    assert invert_tree(new_tree(4, 2, 7, 1, 3, 6, 9)) == new_tree(4, 7, 2, 9, 6, 3, 1)
Example #12
0
from leetcode import TreeNode, new_tree


def min_depth(root: TreeNode) -> int:
    if not root:
        return 0

    def inner(node: TreeNode) -> int:
        if node.left and node.right:
            return min(inner(node.left), inner(node.right)) + 1
        if node.left:
            return inner(node.left) + 1
        if node.right:
            return inner(node.right) + 1
        return 1

    return inner(root)


if __name__ == "__main__":
    assert min_depth(new_tree(3, 9, 20, None, None, 15, 7)) == 2
    assert min_depth(new_tree(1, 2)) == 2
from collections import deque

from leetcode import TreeNode, new_tree


def is_cousins(root: TreeNode, x: int, y: int) -> bool:
    def bfs(node: TreeNode, t: int) -> tuple[int, int]:
        queue = deque()
        queue.append((node, None, 0))
        while (size := len(queue)) > 0:
            for _ in range(size):
                current, father, depth = queue.popleft()
                if current.val == t:
                    return father.val if father is not None else 0, depth
                if current.left is not None:
                    queue.append((current.left, current, depth + 1))
                if current.right is not None:
                    queue.append((current.right, current, depth + 1))
        return -1, -1

    x_father, x_depth = bfs(root, x)
    y_father, y_depth = bfs(root, y)
    return x_father != y_father and x_depth == y_depth


if __name__ == "__main__":
    assert is_cousins(new_tree(1, 2, 3, 4), 4, 3) is False
    assert is_cousins(new_tree(1, 2, 3, None, 4, None, 5), 5, 4) is True
    assert is_cousins(new_tree(1, 2, 3, None, 4), 2, 3) is False
Example #14
0
def binary_tree_path(root: TreeNode) -> List[str]:
    if not root:
        return []

    stack, results = [], []

    def helper(node: TreeNode) -> None:
        nonlocal stack, results

        stack.append(node.val)
        if not node.left and not node.right:
            results.append(list(stack))
        else:
            if node.left:
                helper(node.left)
            if node.right:
                helper(node.right)
        stack.pop()

    helper(root)
    return ["->".join(str(val) for val in result) for result in results]


test(
    binary_tree_path,
    [
        (new_tree(1, 2, 3, None, 5), ["1->2->5", "1->3"]),
    ],
    map_func=sorted_list,
)
Example #15
0
from leetcode import TreeNode, test, new_tree


def convert_bst(root: TreeNode) -> TreeNode:
    acc = 0

    def reversed_inorder(node: TreeNode) -> None:
        nonlocal acc
        if node:
            reversed_inorder(node.right)
            acc = node.val = acc + node.val
            reversed_inorder(node.left)

    reversed_inorder(root)
    return root


test(
    convert_bst,
    [
        (new_tree(5, 2, 13), new_tree(18, 20, 13)),
    ],
)
Example #16
0

def build_tree(inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
    def helper(in_order: List[int],
               post_order: List[int]) -> Optional[TreeNode]:
        if in_order and post_order:
            root_val = post_order[-1]
            for root_offset, val in enumerate(in_order):
                if val == root_val:
                    break
            else:
                root_offset = 0

            root = TreeNode(root_val)
            root.left = helper(in_order[:root_offset],
                               post_order[:root_offset])
            root.right = helper(in_order[root_offset + 1:],
                                post_order[root_offset:-1])
            return root

    return helper(inorder, postorder)


test(
    build_tree,
    [
        ([9, 3, 15, 20, 7], [9, 15, 7, 20, 3
                             ], new_tree(3, 9, 20, None, None, 15, 7)),
    ],
)
Example #17
0
from typing import List

from leetcode import TreeNode, new_tree, test


def inorder_traversal(root: TreeNode) -> List[int]:
    stack, result = [], []

    def push_node(node: TreeNode) -> None:
        while node:
            stack.append(node)
            node = node.left

    push_node(root)
    while stack:
        p = stack.pop()
        result.append(p.val)
        push_node(p.right)
    return result


test(
    inorder_traversal,
    [
        (new_tree(1, None, 2, 3), [1, 3, 2]),
    ],
)
Example #18
0
from leetcode import TreeNode, new_tree


def lowest_common_ancestor(root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
    rv, pv, qv = root.val, p.val, q.val
    if pv > rv and qv > rv:
        return lowest_common_ancestor(root.right, p, q)
    elif pv < rv and qv < rv:
        return lowest_common_ancestor(root.left, p, q)
    else:
        return root


if __name__ == "__main__":
    root1 = new_tree(6, 2, 8, 0, 4, 7, 9, None, None, 3, 5)
    p1 = root1.left
    q1 = root1.right
    assert lowest_common_ancestor(root1, p1, q1) is root1

    p2 = root1.left
    q2 = root1.left.right
    assert lowest_common_ancestor(root1, p2, q2) is p2
Example #19
0
    stack, result, = (
        [],
        [],
    )

    def dfs(node: TreeNode, remaining: int) -> None:
        stack.append(node)
        if node.left or node.right:
            if node.left:
                dfs(node.left, remaining - node.val)
            if node.right:
                dfs(node.right, remaining - node.val)
        else:
            if node.val == remaining:
                path = [n.val for n in stack]
                result.append(path)
        stack.pop()

    dfs(root, target)
    return result


if __name__ == "__main__":
    assert sorted_equals(
        path_sum(new_tree(5, 4, 8, 11, None, 13, 4, 7, 2, None, None, 5, 1), 22),
        [
            [5, 4, 11, 2],
            [5, 8, 4, 5],
        ],
    )
Example #20
0
from leetcode import TreeNode, new_tree


def rob(root: TreeNode) -> int:
    def helper(node: TreeNode) -> tuple[int, int]:
        if not node:
            return 0, 0
        left_max, left_no_rob = helper(node.left)
        right_max, right_no_rob = helper(node.right)
        return (
            max(left_no_rob + right_no_rob + node.val, left_max + right_max),
            left_max + right_max,
        )

    return helper(root)[0]


if __name__ == "__main__":
    assert rob(new_tree(3, 4, 5, 1, 3, None, 1)) == 9
Example #21
0
def zigzag_level_order(root: TreeNode) -> List[List[int]]:
    if not root:
        return []

    level = [root]
    result = []
    reverse = False

    while level:
        values = [node.val for node in level]
        if reverse:
            values.reverse()
        reverse = not reverse
        result.append(values)

        tmp = []
        for node in level:
            if node.left:
                tmp.append(node.left)
            if node.right:
                tmp.append(node.right)
        level = tmp

    return result


test(
    zigzag_level_order,
    [(new_tree(3, 9, 20, None, None, 15, 7), [[3], [20, 9], [15, 7]])],
)
Example #22
0
from leetcode import TreeNode, test, new_tree


def sum_of_left_leaves(root: TreeNode) -> int:
    def helper(node: TreeNode, is_left: bool) -> int:
        if node:
            if node.left or node.right:
                return helper(node.left, True) + helper(node.right, False)
            elif is_left:
                return node.val
        return 0

    return helper(root, False)


test(
    sum_of_left_leaves,
    [
        (new_tree(3, 9, 20, None, None, 15, 7), 24),
    ],
)
Example #23
0
from typing import Optional

from leetcode import TreeNode, new_tree


def merge_trees(lhs: Optional[TreeNode], rhs: Optional[TreeNode]) -> Optional[TreeNode]:
    if lhs and rhs:
        lhs.val += rhs.val
        lhs.left = merge_trees(lhs.left, rhs.left)
        lhs.right = merge_trees(lhs.right, rhs.right)
        return lhs
    elif lhs:
        return lhs
    elif rhs:
        return rhs
    else:
        return None


if __name__ == "__main__":
    assert merge_trees(
        new_tree(1, 3, 2, 5), new_tree(2, 1, 3, None, 4, None, 7)
    ) == new_tree(3, 4, 5, 5, 4, None, 7)
        result.pop()
    return ",".join(result)


def deserialize(data: str) -> Optional[TreeNode]:
    nodes = map(
        lambda t: None if t in ("null", "") else TreeNode(int(t)), data.split(",")
    )
    root = next(nodes)
    if root is None:
        return None

    queue = deque([root])
    is_left = True
    node = None
    for next_node in nodes:
        if next_node:
            queue.append(next_node)
        if is_left:
            node = queue.popleft()
            node.left = next_node
        else:
            node.right = next_node
        is_left = not is_left
    return root


if __name__ == "__main__":
    assert deserialize("1,2,3,null,null,4,5") == new_tree(1, 2, 3, None, None, 4, 5)
    assert serialize(new_tree(1, 2, 3, None, None, 4, 5)) == "1,2,3,null,null,4,5"
Example #25
0
from leetcode import TreeNode, new_tree


def convert_bst(root: TreeNode) -> TreeNode:
    acc = 0

    def reversed_inorder(node: TreeNode) -> None:
        nonlocal acc
        if node:
            reversed_inorder(node.right)
            acc = node.val = acc + node.val
            reversed_inorder(node.left)

    reversed_inorder(root)
    return root


if __name__ == "__main__":
    assert convert_bst(new_tree(5, 2, 13)) == new_tree(18, 20, 13)
Example #26
0
        else:
            temp = stack.pop().left

    result.reverse()
    return result


def postorder_traversal_recursively(root: TreeNode) -> List[int]:
    result = []

    def recurse(node: TreeNode) -> None:
        if node:
            recurse(node.left)
            recurse(node.right)
            result.append(node.val)

    recurse(root)
    return result


tests = [
    new_tree(1, None, 2, 3),
]
for tree in tests:
    expect = postorder_traversal_recursively(tree)
    actual = postorder_traversal(tree)
    if expect != actual:
        message = f"tree: {tree}\nactual: {actual}\nexpect: {expect}"
        print(message, file=sys.stderr)
        sys.exit(1)
Example #27
0
from leetcode import TreeNode, new_tree


def level_order_bottom(root: TreeNode) -> list[list[int]]:
    if not root:
        return []

    queue, result = deque([root]), []
    while queue:
        queue_len = len(queue)
        level = []
        for _ in range(queue_len):
            node = queue.popleft()
            level.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        result.append(level)

    result.reverse()
    return result


if __name__ == "__main__":
    tests = [
        (new_tree(3, 9, 20, None, None, 15, 7), [[15, 7], [9, 20], [3]]),
    ]
    for tree, want in tests:
        assert level_order_bottom(tree) == want
Example #28
0
from leetcode import TreeNode, test, new_tree


def min_depth(root: TreeNode) -> int:
    if not root:
        return 0

    def inner(node: TreeNode) -> int:
        if node.left and node.right:
            return min(inner(node.left), inner(node.right)) + 1
        if node.left:
            return inner(node.left) + 1
        if node.right:
            return inner(node.right) + 1
        return 1

    return inner(root)


test(
    min_depth,
    [
        (new_tree(3, 9, 20, None, None, 15, 7), 2),
        (new_tree(1, 2), 2),
    ],
)
from leetcode import TreeNode, test, new_tree, inorder_traverse


def insert_into_bst(root: TreeNode, val: int) -> TreeNode:
    node, prev, is_left = root, None, False
    while node:
        prev = node
        if val < node.val:
            node, is_left = node.left, True
        else:
            node, is_left = node.right, False

    new_node = TreeNode(val)
    if prev is None:
        return new_node

    if is_left:
        prev.left = new_node
    else:
        prev.right = new_node
    return root


test(
    insert_into_bst,
    [(new_tree(4, 2, 7, 1, 3), 5, [1, 2, 3, 4, 5, 7]), (new_tree(), 1, [1])],
    equals_func=lambda actual, expect: inorder_traverse(actual) == expect,
)
def find_mode(root: TreeNode) -> List[int]:
    result, values, prev_value, count, max_count = [], [], 0, 0, 0

    def update() -> None:
        nonlocal prev_value, count, max_count
        max_count = max(max_count, count)
        values.append((prev_value, count))

    def inorder(node: TreeNode) -> None:
        nonlocal prev_value, count, max_count
        if not node:
            return
        inorder(node.left)
        if node.val == prev_value:
            count += 1
        else:
            update()
            prev_value, count = node.val, 1
        inorder(node.right)

    inorder(root)
    update()
    for value, count in values:
        if count > 0 and count == max_count:
            result.append(value)
    return result


test(find_mode, [(new_tree(1, None, 2, 2), [2]), (new_tree(0), [0])])