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), ], )
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
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
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), ], )
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
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]
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), ], )
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)
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
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, )
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)), ], )
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)), ], )
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]), ], )
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
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], ], )
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
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]])], )
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), ], )
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"
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)
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)
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
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])])