# Also do the 2026.05.06 homework # # Implement insertion, removal and search in a binary search tree. # Properties of binary search trees: # - Each node has at most two children # - The left subtree of a node contains only nodes with values less than the node's value # - The right subtree of a node contains only nodes with values greater than the node's value # # Example of what such a tree would look like: # 10 # / \ # 5 15 # / \ \ # 3 7 20 # # Example of an *invalid* tree: # 10 # / \ # 5 15 # / \ \ # 3 7 20 # \ # 12 # # Don't worry about balancing the tree for now, we will cover that later class Node: def __init__(self, value, left=None, right=None): self.value = value self.left: Node | None = left self.right: Node | None = right class BinarySearchTree: def __init__(self, root=None): self.root: Node | None = root def find_prev_node(self, value): prev_node: Node | None = None node = self.root while node is not None: prev_node = node if node.value >= value: node = node.left else: node = node.right return prev_node def find_existing_node_and_parent(self, value): prev_node: Node | None = None node = self.root while node is not None: if value == node.value: return node, prev_node prev_node = node if value < node.value: node = node.left else: node = node.right return None, None def insert(self, value): prev_node = self.find_prev_node(value) if prev_node is None: self.root = Node(value) return if prev_node.value >= value: prev_node.left = Node(value) else: prev_node.right = Node(value) def remove(self, value): node, prev_node = self.find_existing_node_and_parent(value) if prev_node is None and node is not None: self.root = None return elif node is not None: if prev_node.value >= value: prev_node.left = None else: prev_node.right = None def search(self, value): node = self.root while node is not None: if value == node.value: return node elif value < node.value: node = node.left else: node = node.right return None print("================ Inserts ================") test_tree = BinarySearchTree() test_tree.insert(5) test_tree.insert(3) print(test_tree.root.left.value) test_tree.insert(4) test_tree.insert(6) print(test_tree.root.left.right.value) print(test_tree.root.right.value) print("================ Searches ================") # is the search result giving us the expected node? print(test_tree.search(4) == test_tree.root.left.right) print(test_tree.search(6) == test_tree.root.right) print(test_tree.search(20) is None) print("================ Removals ================") test_tree.remove(4) print(test_tree.root.left.right) test_tree.remove(6) print(test_tree.root.right) test_tree.remove(3) print(test_tree.root.left) test_tree.remove(5) print(test_tree.root)