A Binary Search Tree, also called an ordered or sorted binary tree, is a rooted binary tree data structure where each Node has up to two children. All values in the left subtree of a node are less than the value at the tree’s root, and all values in the right subtree of a node are greater than or equal to the value at the root. Therefore, both subtrees must also be binary search trees.
class BinarySearchTree:# We create our node class as an internal to our BST classclass __Node:def __init__(self, val, left=None, right=None):self.val = valself.left = leftself.right = rightdef getVal(self):return self.valdef setVal(self, newVal):self.val = newValdef getLeft(self):return self.leftdef getRight(self):return self.rightdef setLeft(self,newLeft):self.left = newLeftdef setRight(self, newRight):self.right = newRight# Recursive traversal of the tree, calls __iter__ method on left subtree, yielding all elements,# then the root of the tree is yielded, then the right subtree, resulting in an inorder traversal of the tree.def __iter__(self):if self.left != None:for elem in self.left:yield elemyield self.valif self.right != None:for elem in self.right:yield elem# BST Class methodsdef __init__(self):self.root = Nonedef insert(self, val):# The insert function is recursive. We do not pass a self-parameter.# Static function, not class methoddef __insert(root, val):if root == None:return BinarySearchTree.__Node(val)if val < root.getVal():# Recursively calls __insert on subtree,# Reassign the reference after inserting a new value into the treeroot.setLeft(__insert(root.getLeft(), val))else:# Recursively calls __insert on subtree,# Reassign the reference after inserting a new value into the treeroot.setRight(__insert(root.getRight(), val))return root# After inserting a new value, reassign root reference(possibly to same Node)# to the new tree after inserting the new value.# Each time the __insert function is called, a new tree is returnedself.root = __insert(self.root, val)# Calls __iter__ else returns empty arraydef __iter__(self):if self.root != None:return self.root.__iter__()else:return [].__iter__()def main():s = input("Enter a list of numbers: ")lst = s.split()tree = BinarySearchTree()for x in lst:tree.insert(float(x))for x in tree:print(x)if __name__ == "__main__":main()
Our binary search tree produces a sorted list of values when traversed.
Enter a list of numbers: 5 8 2 1 4 9 6 71.02.04.05.06.07.08.09.0Process finished with exit code 0
Our insert function is given a tree, which initially is None and the __insert function returns a new tree with our new value. Finally, the root is set equal to this new tree.
Each time the __insert function is called, a new tree is returned, and our root is reassigned, often to the same Node. It doesn’t hurt our code to reassign references, and our
code works quite nicely. Our recursive __insert always reassigns the reference after inserting the new value into the tree. Finally, the root
reference is reassigned to the new tree after inserting the new value.
Our __iter__ uses recursion to traverse the tree. After all the elements in the left subtree are yielded, the root value is yielded, then the values in the right
subtree are yielded, resulting in an inorder traversal of the tree.
Worse case insertion complexity is O(n^2) with an average case of O(log n), or O(n log n) for the insertion of n items.
Legal Stuff