Analogous to a binary search algorithm, we traverse left if our value (the value that we are searching for) is lower than node value, or traverse right otherwise, until we have found a node value that is equal to our value.
During traversal, if we ended up in a null, we can conclude that there is no value equal to our value in the binary search tree.
Implementation
from dataclasses import dataclassfrom typing import Any, Protocol, Selfclass Comparable(Protocol): def __lt__(self, value: Any, /) -> bool: ...# || <zmnIVzYkXU|JtQexJSkQN>type _Node = Node | None@dataclassclass Node[T]: data: T left: _Node = None right: _Node = None @staticmethod def traverse_tree_inorder(root: _Node) -> None: if root is None: return Node.traverse_tree_inorder(root.left) print(root.data) Node.traverse_tree_inorder(root.right) @staticmethod def traverse_tree_postorder(root: _Node) -> None: if root is None: return Node.traverse_tree_postorder(root.left) Node.traverse_tree_postorder(root.right) print(root.data) @staticmethod def traverse_tree_preorder(root: _Node) -> None: if root is None: return print(root.data) Node.traverse_tree_preorder(root.left) Node.traverse_tree_preorder(root.right)# || <JtQexJSkQN|AcIj9MCcYY>class BinarySearchTree[T: Comparable]: root: Node[T] def insert(self, node: Node[T]) -> Self: """Insert a node to its appropriate position in the binary search tree.""" if not self._is_set_root(): self.root = node return self data = node.data curr = self.root while True: if data < curr.data: # go left if curr.left is None: curr.left = node break curr = curr.left elif data > curr.data: # go right if curr.right is None: curr.right = node break curr = curr.right else: raise ValueError(f"Value of {data} already exists in the tree.") return self def delete(self, value: T) -> None: """Delete a node with the given value from the binary search tree. Args: value: The value to delete from the tree """ self.root = self._delete_helper(self.root, value) # type: ignore def _delete_helper(self, root: Node[T] | None, value: T) -> Node[T] | None: """Helper method to recursively delete a node. Parameters ---------- root : `Node[T] | None` Current node being examined. value : `T` Value to delete Returns ------- `Node[T] | None` Updated root node after deletion """ # Base case: if tree is empty or value not found if root is None: return None # Recursively search for the node to delete if value < root.data: root.left = self._delete_helper(root.left, value) elif value > root.data: root.right = self._delete_helper(root.right, value) else: # Node found - handle the three cases # Case 1: Leaf node (no children) if root.left is None and root.right is None: return None # Case 2a: Only has right child if root.left is None: return root.right # Case 2b: Only has left child if root.right is None: return root.left # Case 3: Has both children # Find the smallest value in right subtree (successor) successor_value = self.successor(root.right).data # Copy successor value to current node root.data = successor_value # Delete the successor root.right = self._delete_helper(root.right, successor_value) return root def display(self) -> None: self.root.traverse_tree_inorder(self.root) def search(self, value: T) -> bool: """Searches for a value in the tree. Parameters ---------- value : `T` Value to search. Returns ------- `bool` True if the value is found in the tree. False if not. """ curr = self.root while True: if curr is None: return False if curr.data == value: return True if value < curr.data: # go left curr = curr.left else: # go right curr = curr.right def successor(self, root: Node) -> Node[T]: """Find least value below the right child of this root node""" if root.right is None: return root.data curr = root.right while True: if curr.left is None: return curr curr = curr.left def predecessor(self, root: Node) -> Node[T]: """Find a node with the greatest value below the left child of this root node""" if root.left is None: return root.data curr = root.left while True: if curr.right is None: return curr curr = curr.right def _is_set_root(self) -> bool: """Checks if the root node is set and is not None.""" ret = hasattr(self, "root") and self.root is not None return ret# initbst: BinarySearchTree[int] = BinarySearchTree()# insertbst.insert(Node(4))bst.insert(Node(2))bst.insert(Node(6))bst.insert(Node(1))bst.insert(Node(3))bst.insert(Node(5))bst.insert(Node(7))# displaybst.display()# searchprint(bst.search(3))# deleteprint(bst.delete(3))# displaybst.display()