Package: pybinarytree
DescriptionThe BinaryTree class is a general-purpose sorted list.Installationpip install pybinarytreeImplementationThe BinaryTree class can accept either no object, an individual object, or a list/tuple of objects.frompybinarytreeimportBinaryTree# Initialize without any values:tree=BinaryTree()print(tree)# []# Initializing with a single value:tree=BinaryTree(5)print(tree)# [5]# Initializing with a list:integers=[4,7,2,10,3,9,1,5,6,8]tree=BinaryTree(integers)print(tree)# [1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10]# Initializing with a tuple:integers=(11,18,14,12,19,20,16,13,17,15)tree=BinaryTree(integers)print(tree)# [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]Sorting Custom ClassesThe BinaryTree class is meant to sort any type of object, not just numeric types or strings.To allow for sorting, the class object must have defined comparison methods to describe which values are less than, less than or equal to, equal to, greater than or equal to, or greater than other values. For example:classMySortableClass:def__init__(self,value:int):self.value=valuedef__lt__(self,other)->bool:returnself.value<other.valuedef__le__(self,other)->bool:returnself.value<=other.valuedef__eq__(self,other)->bool:returnself.value==other.valuedef__ge__(self,other)->bool:returnself.value>=other.valuedef__gt__(self,other)->bool:returnself.value>other.valuedef__str__(self)->str:returnstr(self.value)Sorting Multiple ClassesThe BinaryTree can sort multiple types of items as long as their comparison methods can account for it. For example, floats and integers can be compared with each other, but are not the same type.frompybinarytreeimportBinaryTreevalues=[1,1.5,2,2.5,3,3.5,4,4.5,5]tree=BinaryTree(values)print(tree.to_display_string())"""┌────── 3 ─────────┐┌────── 2 ─┐       ┌────── 4.5 ─┐1 ─┐       2.5     3.5 ─┐       51.5                  4"""The same idea should apply to any custom classes that are given to the BinaryTree class, just so long as they can work together.Class MethodsBinaryTree.add(new_value: Any)After initialization, the only way to add new values to the tree is to pass the value to BinaryTree.add().frompybinarytreeimportBinaryTree# Initialize the tree.integers=[1,2,3,4,5]tree=BinaryTree(integers)print(tree.to_display_string())"""┌──── 3 ─┐1 ─┐     4 ─┐2        5"""# Add individual values to the tree.forvaluein[6,7,8,9,10]:tree.add(value)print(tree.to_display_string())"""┌──── 3 ─┐1 ─┐     4 ─┐2        5 ─┐6 ─┐7 ─┐8 ─┐9 ─┐10"""# Add a list (or tuple) of new items.tree.add([0,-1,-2,-3,-4,-5])print(tree.to_display_string())"""┌──── 3 ─┐┌─ 1 ─┐     4 ─┐┌── 0     2        5 ─┐┌── -1                    6 ─┐┌── -2                           7 ─┐┌── -3                                  8 ─┐┌── -4                                         9 ─┐-5                                                10"""BinaryTree.balance()Balancing the tree takes time, and therefore does not happen each time a new value is added. This means that the tree's depth (or the number of steps needed to reach the furthest values from the root node) increases, but saves time in the short-term.Adding too many values without balancing will decrease the efficiency of the tree. If desired, the tree can be re-balanced to optimize future performance.frompybinarytreeimportBinaryTreetree=BinaryTree()forvaluein[1,2,3,4,5,6,7,8,9,10]:tree.add(value)print(tree.to_display_string())"""1 ─┐2 ─┐3 ─┐4 ─┐5 ─┐6 ─┐7 ─┐8 ─┐9 ─┐10"""tree.balance()print(tree.to_display_string())"""┌─────── 6 ───────┐┌──── 3 ─┐        ┌──── 9 ─┐1 ─┐     4 ─┐     7 ─┐     102        5        8"""BinaryTree.find_between(low_val: Any, high_val: Any)Accepts two values (low_val and high_val) where low_val <= high_val.Returns a sorted list of X items where low_val <= X <= high_val.frompybinarytreeimportBinaryTreeintegers=[1,2,3,3,4,5,6,7,8,9,10]# Multiple 3'stree=BinaryTree(integers)print(tree.find_between(3,5))# [3, 3, 4, 5]print(tree.find_between(7,9))# [7, 8, 9]print(tree.find_between(100,200))# []BinaryTree.find_equal_to(value: Any)Accepts a value.Returns a sorted list of X items where X == value.frompybinarytreeimportBinaryTreeintegers=[1,2,3,3,4,5,6,7,8,9,10]# Multiple 3'stree=BinaryTree(integers)print(tree.find_equal_to(8))# [8]print(tree.find_equal_to(3))# [3, 3]print(tree.find_equal_to(100))# []BinaryTree.find_greater_than(value: Any)Accepts a value.Returns a sorted list of X items where value < X.frompybinarytreeimportBinaryTreeintegers=[1,2,3,3,4,5,6,7,8,9,10]# Multiple 3'stree=BinaryTree(integers)print(tree.find_greater_than(5))# [6, 7, 8, 9, 10]print(tree.find_greater_than(0))# [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10]print(tree.find_greater_than(100))# []BinaryTree.find_less_than(value: Any)Accepts a value.Returns a sorted list of X items where X < value.frompybinarytreeimportBinaryTreeintegers=[1,2,3,3,4,5,6,7,8,9,10]# Multiple 3'stree=BinaryTree(integers)print(tree.find_less_than(5))# [1, 2, 3, 3, 4]print(tree.find_less_than(0))# []print(tree.find_less_than(100))# [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10]BinaryTree.remove_all(value: Any)Accepts a value or list/tuple of values.Deletes all copies of the value from the tree (if any exist) and returns None.frompybinarytreeimportBinaryTreeintegers=[1,2,3,3,3,3,3,4,5,6,7,8,9,10]# Multiple 3'stree=BinaryTree(integers)print(tree.to_display_string())"""┌──────────── 6 ───────┐┌──── 3 (x5) ─┐        ┌──── 9 ─┐1 ─┐          4 ─┐     7 ─┐     102             5        8"""# Remove all of the 3's from the tree.tree.remove_all(3)print(tree.to_display_string())"""┌────────── 6 ───────┐1 ─┐           ┌──── 9 ─┐2 ─┐        7 ─┐     104 ─┐        85"""# Remove a list (or tuple) of values, with each one removing all copies.tree.add([7for_inrange(100)])tree.remove_all([7,9,10])print(tree.to_display_string())"""┌────────── 6 ─┐1 ─┐           82 ─┐4 ─┐5"""BinaryTree.remove_one(value: Any)Accepts a value or list/tuple of values.Deletes one copy of the value from the tree (if it exists) and returns None.frompybinarytreeimportBinaryTreeintegers=[1,2,3,3,4,5,6,7,8,9,10]# Multiple 3'stree=BinaryTree(integers)print(tree.to_display_string())"""┌──────────── 6 ───────┐┌──── 3 (x2) ─┐        ┌──── 9 ─┐1 ─┐          4 ─┐     7 ─┐     102             5        8"""# Remove one of the two 3's from the tree.tree.remove_one(3)print(tree.to_display_string())"""┌─────── 6 ───────┐┌──── 3 ─┐        ┌──── 9 ─┐1 ─┐     4 ─┐     7 ─┐     102        5        8"""# Remove a list (or tuple) of values, with each one removing one copy.tree.remove_one([7,9,10])print(tree.to_display_string())"""┌─────── 6 ─┐┌──── 3 ─┐        81 ─┐     4 ─┐2        5"""BinaryTree.reversed()This method returns a generator that iterates through the tree in reverse order.frompybinarytreeimportBinaryTreeintegers=[1,2,3,4,5,6,7,8,9,10]tree=BinaryTree(integers)print([xforxintree])# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]print([xforxintree.reversed()])# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]BinaryTree.to_display_string()In any situation where the tree structure should be displayed, the "to_display_string()" method can be called to retrieve a string.frompybinarytreeimportBinaryTreeintegers=[1,2,3,4,5,6,7,8,9,10]tree=BinaryTree(integers)print(tree.to_display_string())"""┌─────── 6 ───────┐┌──── 3 ─┐        ┌──── 9 ─┐1 ─┐     4 ─┐     7 ─┐     102        5        8"""There are a few things to note about how the tree generates a response:The tree uses each item's __str__ method to convert it into a string, then replaces any new line characters ("\n") with spaces (" ") and shortens it to a maximum length of 64 characters. This is to prevent the final result from becomming so complex that it makes reading it difficult.Multiple values that are equal (as determined by the == operator) are simplified to display a count:frompybinarytreeimportBinaryTreeintegers=[1,2,3,3,4,5,6,7,8,9,10]# Multiple 3's.tree=BinaryTree(integers)print(tree.to_display_string())"""┌──────────── 6 ───────┐┌──── 3 (x2) ─┐        ┌──── 9 ─┐1 ─┐          4 ─┐     7 ─┐     102             5        8"""When equal values are present in a tree, the first one is converted into the string used by the "to_display_string()" method. All subsequent items will have their strings replaced with a total count to simplify the output.An empty tree will return an empty string.frompybinarytreeimportBinaryTreetree=BinaryTree()print(tree.to_display_string())# Result: ""BinaryTree.to_flipped_display_string()Similar to "BinaryTree.to_display_string()" except the tree branches upwards rather than downwards.frompybinarytreeimportBinaryTreeintegers=[1,2,3,4,5,6,7,8,9,10]tree=BinaryTree(integers)print(tree.to_display_string())"""┌─────── 6 ───────┐┌──── 3 ─┐        ┌──── 9 ─┐1 ─┐     4 ─┐     7 ─┐     102        5        8"""print(tree.to_flipped_display_string())"""2        5        81 ─┘     4 ─┘     7 ─┘     10└──── 3 ─┘        └──── 9 ─┘└─────── 6 ───────┘"""ExceptionsNotComparableExceptionA NotComparableException is raised any time a new item given to a tree cannot be compared with items already existing within the tree.There are a couple of reasons why a NotComparableException could be raised, including but not limited to:The object being sorted does not have defined comparison methods. (See "Sorting Custom Classes" for more info.)The object is of a different type than other objects already in the tree, and the comparison methods are incompatible with each other.If you know that the objects should be comparable, but a NotComparableException is raised anyway, check the object's comparison methods. If they are missing or invalid, that might be the source of the issue. All of the following should return either True or False (boolean type) to prevent a NotComparableException:x=MySortableClass()_=x<x# x.__lt__(...)_=x<=x# x.__le__(...)_=x==x# x.__eq__(...)_=x>=x# x.__ge__(...)_=x>x# x.__gt__(...)