def __init__(self, elements=None):
     """Initialize this binary tree with the given data."""
     self.tree = BinarySearchTree()
     if elements is not None:
         for element in elements:
             self.add(element)
def tree_sort(items):
    tree = BinarySearchTree(items)
    items[:] = tree.items_in_order()
class TreeSet(object):
    """Initialize a new empty set structure, and add each element if a sequence is given"""
    def __init__(self, elements=None):
        self.tree = BinarySearchTree()
        self.element = BinaryTreeNode
        if elements is not None:
            for element in elements:
                self.add(element)

    @property
    def size(self):
        """Property that tracks the number of elements in constant time"""
        return self.tree.size

    def contains(self, element):
        """return a boolean indicating whether element is in this set
        Running time: O(log n) since we just search through a binary tree"""
        return self.tree.contains(element)

    def add(self, element):
        """add element to this set, if not present already
        Running time: O(log n) because we must binary search"""
        if self.tree.contains(element):
            raise KeyError(f'{element} is already in tree.')
        else:
            self.tree.insert(element)

    def remove(self, element):
        """remove element from this set, if present, or else raise KeyError
        Running time: 
        Best Case: O(h) because we have to either scale the tree or move everything down
        once found.
        Worst Case: O(n) because the element could be the last we check in the tree."""
        if self.tree.contains(element) != True:
            raise KeyError(f'No such element exists: {element}')
        else:
            self.tree.delete(element)

    def union(self, other_set):
        """return a new set that is the union of this set and other_set
        Best and worst case: (m + n) * log m because we add the length of each set to the time of the .add calls"""
        # m log m + n*2logm = (m + 2n) * log m =
        # (m + n) * log m
        new_set = TreeSet()
        for element in self.tree.items_pre_order():  # O(h(self))
            new_set.add(element)  # + O(m)

        # Adds remaining other_set elements
        for element in other_set.tree.items_pre_order():
            if not new_set.contains(element):  # O(log m)
                new_set.add(element)  # +O(log m)

        return new_set

    def intersection(self, other_set):
        """return a new set that is the intersection of this set and other_set
        Best case/worst case: O(log n) due to binary search"""
        new_set = TreeSet()
        for element in self.tree.items_in_order():
            if other_set.contains(element):
                new_set.add(element)
        return new_set

    def difference(self, other_set):
        """return a new set that is the difference of this set and other_set
        Best/Worst case: O(n) because we require checking all nodes"""
        new_set = TreeSet()
        for element in self.tree.items_in_order():
            new_set.add(element)

        for element in other_set.tree.items_in_order():
            if new_set.contains(element):
                new_set.remove(element)
        return new_set

    def is_subset(self, other_set):
        """return a boolean indicating whether other_set is a subset of this set
        Best case:e O(1), incase the size is bigger than other_set and we don't need to do anything.
        Worst case: O(n), if we must traverse through all nodes"""
        if self.size > other_set.size:
            return False

        for element in other_set.tree.items_in_order():
            if not self.contains(element):
                return False

        return True
Example #4
0
 def DISABLED_test_delete_with_7_items(self):
     # Create a complete binary search tree of 7 items in level-order
     items = [4, 2, 6, 1, 3, 5, 7]
     tree = BinarySearchTree(items)
Example #5
0
 def test_items_level_order_with_7_numbers(self):
     # Create a complete binary search tree of 7 items in level-order
     items = [4, 2, 6, 1, 3, 5, 7]
     tree = BinarySearchTree(items)
     # Ensure the level-order traversal of tree items is ordered correctly
     assert tree.items_level_order() == [4, 2, 6, 1, 3, 5, 7]
 def __init__(self, items=None):
     """Initialize a new Set"""
     self.tree = BinarySearchTree(items)
 def __init__(self, init_size=8):
     self.buckets = [BinarySearchTree() for i in range(init_size)]
     self.size = 0
Example #8
0
 def test_eq(self):
     assert BinarySearchTree({1, 2, 3}) == BinarySearchTree({1, 2, 3})
     assert BinarySearchTree([1, 2, 3]) == BinarySearchTree((1, 2, 3))
     assert BinarySearchTree([1, 2, 3]) == BinarySearchTree([3, 2, 1])
Example #9
0
    def test_delete(self):

        ## Delete leaf and root
        bst = BinarySearchTree(['E'])
        bst.delete('E')
        assert bst.items_in_order() == []

        ## Delete left leaf
        bst = BinarySearchTree(['C', 'A', 'B', 'E'])

        bst.delete('B')
        assert bst.items_in_order() == ['A', 'C', 'E']

        ## Delete right leaf
        bst = BinarySearchTree(['A', 'B', 'C', 'E'])

        bst.delete('E')
        assert bst.items_in_order() == ['A', 'B', 'C']

        ## Delete node with only direct child on left
        bst = BinarySearchTree(['C', 'B', 'A', 'E'])

        bst.delete('B')
        assert bst.items_in_order() == ['A', 'C', 'E']

        ## Delete node with only direct child on right
        bst = BinarySearchTree(['C', 'A', 'B', 'E'])

        bst.delete('A')
        assert bst.items_in_order() == ['B', 'C', 'E']
Example #10
0
 def test_insert_duplicate_monotonic(self):
     bst = BinarySearchTree()
     bst.insert('A')
     assert bst.items_in_order() == ['A']
     bst.insert('A')
     assert bst.items_in_order() == ['A']
     bst.insert('B')
     assert bst.items_in_order() == ['A', 'B']
     bst.insert('C')
     assert bst.items_in_order() == ['A', 'B', 'C']
     bst.insert('A')
     assert bst.items_in_order() == ['A', 'B', 'C']
     bst.insert('B')
     assert bst.items_in_order() == ['A', 'B', 'C']
     bst.insert('C')
     assert bst.items_in_order() == ['A', 'B', 'C']
     bst.insert('D')
     assert bst.items_in_order() == ['A', 'B', 'C', 'D']
Example #11
0
    def test_eq_edges(self):
        assert BinarySearchTree() == BinarySearchTree()
        assert BinarySearchTree([]) == BinarySearchTree(())
        assert BinarySearchTree([1, 1, 1, 1, 1]) == BinarySearchTree((1, ))

        assert BinarySearchTree() != BinarySearchTree((1, 2, 3))
        assert BinarySearchTree([]) != BinarySearchTree((1, 2, 3))
        assert BinarySearchTree([1, 2, 3]) != BinarySearchTree(())
Example #12
0
 def test_insert_duplicate(self):
     bst = BinarySearchTree()
     bst.insert('A')
     assert bst.items_in_order() == ['A']
     bst.insert('A')
     assert bst.items_in_order() == ['A']
     bst = BinarySearchTree()
     bst.insert('B')
     assert bst.items_in_order() == ['A', 'B']
     bst.insert('A')
     assert bst.items_in_order() == ['A', 'B']
     bst.insert('Z')
     assert bst.items_in_order() == ['A', 'B', 'Z']
     bst.insert('F')
     assert bst.items_in_order() == ['A', 'B', 'F', 'Z']
     bst.insert('Z')
     assert bst.items_in_order() == ['A', 'B', 'Z']
def main():

    tree = BinarySearchTree([1, 2, 3, 4, 5, 6])
    delete_all_leaves(tree.root)
Example #14
0
def tree_sort(items):
    item_tree = BinarySearchTree(items)
    print(item_tree.items_in_order())
    print(len(item_tree.items_in_order()), len(items))
    items[:] = item_tree.items_in_order()
Example #15
0
 def __init__(self, items=None):
     self.tree = BinarySearchTree()
     if items is not None:
         for item in items:
             self.add(item)
    def test_height_and_size(self):
        # Let's try for a balanced tree first
        items = BinarySearchTree([4])
        assert items.height() == 0
        assert items.size == 1
        # Height 1
        items.insert(2)  # Insert item, tree grows to 1
        assert items.height() == 1
        assert items.size == 2
        items.insert(6)  # Insert item 2; level is full, tree remains 1
        assert items.height() == 1
        assert items.size == 3
        # Height 2
        items.insert(1)
        assert items.height() == 2
        assert items.size == 4
        items.insert(3)
        assert items.height() == 2
        assert items.size == 5
        items.insert(5)
        assert items.height() == 2
        assert items.size == 6
        items.insert(7)
        assert items.height() == 2
        assert items.size == 7

        # Unbalanced tree
        items = BinarySearchTree([1, 2])
        assert items.height() == 1
        assert items.size == 2
        items.insert(3)
        assert items.height() == 2
        assert items.size == 3
        items.insert(4)
        assert items.height() == 3
        assert items.size == 4
        items.insert(6)
        assert items.height() == 4
        assert items.size == 5
        items.insert(7)
        assert items.height() == 5
        assert items.size == 6
        items.insert(5)  # Let's throw a left branching for good measure
        assert items.height() == 5
        assert items.size == 7
class TreeSet(object):
    """Class for sets implemented with binary trees"""
    def __init__(self, items=None):
        """Initialize a new Set"""
        self.tree = BinarySearchTree(items)
        # self.size = self.tree.size

    def __repr__(self):
        """Return a string representation of this set"""
        items = ['({!r})'.format(item) for item in self.tree.items_in_order()]
        return ', '.join(items)

    def __iter__(self):
        """Allow the set to be iterated over (i.e. in for loops)"""
        return iter([value for value in self.tree.items_level_order()])

    def __eq__(self, other):
        """Allow sets to be compared to other sets"""
        return self.tree.items_in_order() == other.tree.items_in_order()

    @property
    def size(self):
        """Use tree size property"""
        return self.tree.size

    def contains(self, item):
        """Return a boolean indicating whether item is in this set
        Best case time complexity: O(1) if item is in the tree root
        Worst case: O(logn) if item is a leaf
        """
        return self.tree.search(item) is not None

    def add(self, item):
        """Add the item to the set if not present
        Best case time complexity: O(1) if tree is empty
        Worst case: O(n) if tree is poorly balanced
        """
        if not self.contains(item):
            self.tree.insert(item)

    def remove(self, item):
        """Remove element from this set, if present, or else raise KeyError
        Best case time complexity: O(1) if item is in the tree root
        Worst case: O(logn) if item isn't there
        """
        if not self.contains(item):
            raise KeyError("Item not found")
        else:
            self.tree.delete(item)

    def union(self, other_set):
        """Return a new set that is the union of this set and other_set
        Best and worst case time complexity: O(n+m), where n is the size of
        self, and m is the size of other_set, because every item has to
        be accounted for
        """
        new_set = TreeSet(self)
        for item in other_set:
            new_set.add(item)
        return new_set

    def intersection(self, other_set):
        """Return a new set that is the intersection of this and other_set
        Best and worst case time complexity: O(m) where m is the size of
        other_set, because it iterates over the other set.
        """
        new_set = TreeSet()
        for item in other_set:
            if self.contains(item):
                new_set.add(item)
        return new_set

    def difference(self, other_set):
        """Return a new set that is the difference of this set and other_set
        Best and worst case time complexity: O(n + logm) where n is the size of
        self, and m is the size of other_set, because it iterates over self and
        checks if other_set contains the items
        """
        new_set = TreeSet()
        for item in self:
            if not other_set.contains(item):
                new_set.add(item)
        return new_set

    def is_subset(self, other_set):
        """Return a boolean indicating if other_set is a subset of this
        Best case time complexity: O(1) if other_set is larger than self
        Worst case: O(m) where m is the size of other_set, as it has to
        iterate over each element of it
        """
        if other_set.size > self.size:
            return False

        for item in other_set:
            if not self.contains(item):
                return False

        return True
    def test_delete(self):
        items = [4, 2, 6, 1, 3, 5, 7]
        # Balanced tree
        tree = BinarySearchTree(items)
        assert tree.items_level_order() == [4, 2, 6, 1, 3, 5, 7]
        assert tree.size == 7
        # Delete leaves
        tree.delete(7)
        assert tree.items_level_order() == [4, 2, 6, 1, 3, 5]
        assert tree.size == 6
        tree.delete(1)
        assert tree.items_level_order() == [4, 2, 6, 3, 5]
        assert tree.size == 5
        # Delete nodes with one branch
        tree.delete(2)
        assert tree.items_level_order() == [4, 3, 6, 5]
        assert tree.size == 4
        tree.delete(6)
        assert tree.items_level_order() == [4, 3, 5]
        assert tree.size == 3

        # Reset
        tree = BinarySearchTree(items)
        assert tree.items_level_order() == [4, 2, 6, 1, 3, 5, 7]
        assert tree.size == 7
        # Delete double branch
        tree.delete(2)
        assert tree.items_level_order() == [4, 1, 6, 3, 5, 7]
        assert tree.size == 6
        tree.delete(6)
        assert tree.items_level_order() == [4, 1, 5, 3, 7]
        assert tree.size == 5

        # Reset
        tree = BinarySearchTree(items)
        assert tree.items_level_order() == [4, 2, 6, 1, 3, 5, 7]
        assert tree.size == 7
        # Delete root
        tree.delete(4)
        print(tree.items_level_order())
        assert tree.items_level_order() == [3, 2, 6, 1, 5, 7]

        # Larger tree
        items = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]
        tree = BinarySearchTree(items)
        tree.delete(8)
        assert tree.items_level_order() == [
            7, 4, 12, 2, 6, 10, 14, 1, 3, 5, 9, 11, 13, 15
        ]
        tree.delete(7)
        print(tree.items_level_order())
        assert tree.items_level_order() == [
            6, 4, 12, 2, 5, 10, 14, 1, 3, 9, 11, 13, 15
        ]
        tree.delete(6)
        print(tree.items_level_order())
        assert tree.items_level_order() == [
            5, 4, 12, 2, 10, 14, 1, 3, 9, 11, 13, 15
        ]
Example #19
0
    def test_delete(self):
        """
             10
           /    \
          5     15
         / \   /  \
        3  8  14  20
          / \
         7   9
        """
        items = [10, 5, 15, 20, 14, 3, 8, 7, 9]
        tree = BinarySearchTree()
        for item in items:
            tree.insert(item)

        # First Case: Node we want to remove has two children
        tree.remove(10)
        """
             20
           /    \
          5     15
         / \   /  
        3  8  14  
          / \
         7   9
        """
        assert tree.root.data == 20

        with self.assertRaises(ValueError):
            tree.remove(10)

        # Second Case: Node we want to remove has no children, we a leaf
        tree.remove(3)
        """
             20
           /    \
          5     15
           \   /  
           8  14  
          / \
         7   9
        """

        with self.assertRaises(ValueError):
            tree.remove(3)

        # Third: Node we want to remove has one child
        tree.remove(5)
        """
             20
           /    \
          8      15
         / \    /  
        7   9  14  
        """

        print(tree.items_in_order())
        tree.remove(14)
        tree.remove(15)
        tree.remove(20)

        assert tree.root.data == 8

        assert tree.root.left.data == 8
Example #20
0
class CallRouter(object):
    def __init__(self, phone_numbers_path, route_prices_path):
          # Turn txt files (data sources) into python objects
          self.phone_numbers = self.parse_phone_numbers(phone_numbers_path)
          # Binary tree of prefixes. Using a BST allows us to find longest prefix as fast as possible
          self.prefixes = BinarySearchTree()
          # Contains best price per unique prefix. Using a dict allows quick fetch AND update
          self.prices = {}
          # Parse routes which populates self.prefixes and self.prices
          self.parse_routes(route_prices_path)

    def turn_txt_file_into_array(self, path_to_file):
        """Turns txt file into list without '\n'"""
        file = open(path_to_file, 'r')
        file_content = file.read() # string representation of .txt file
        file.close()
        array = file_content.split('\n')
        array.pop() # remove last item of array which is empty
        return array

    def parse_phone_numbers(self, phone_numbers_path):
        """Turns txt file into list of phone numbers"""
        return self.turn_txt_file_into_array(phone_numbers_path)
    
    def parse_routes(self, route_prices_path):
        """ Goes through route_prices_path and creates a binary tree """
        # Parse .txt file
        routes = self.turn_txt_file_into_array(route_prices_path)

        # For every route
        for route in routes:
            # get prefix and price (separated by a comma)
            prefix, price = route.split(',')

            if self.prefixes.contains(prefix):
                # Check if price is cheaper
                if self.prices[prefix] > price:
                    self.prices[prefix] = price
            else: # We've never seen prefix before
                self.prefixes.insert(prefix) # insert prefix into our list of prefixes
                self.prices[prefix] = price # log the cost for that prefix

    def get_routing_cost(self, phone_number):
        """Find longest matching prefix and return cheapest cost
        Since routes is a binary tree, we only remember cheapest cost for identical prefix"""
        last_digit_idx = len(str(phone_number)) - 1
        # Search for full phone number inside prefix, then remove one digit at a time
        while last_digit_idx > 0:
            substring = phone_number[0:last_digit_idx]
            if self.prefixes.contains(substring):
                return self.prices[substring]
            last_digit_idx -= 1
        
        # If we have no matching routes, return 0
        else:
            return 0
    
    def save_routing_costs(self, phone_numbers):
        result = []

        for number in phone_numbers:
            cost = self.get_routing_cost(number)
            line = "{},{}".format(number, cost)
            result.append(line)
            # save number and cost to text file
        return result
def main():
    
    tree = BinarySearchTree(['B', 'A', 'C'])
    delete_bt(tree.root)
Example #22
0
class TreeSet:
    def __init__(self, elements=None):
        """initialize a new empty set structure, and add each element if a sequence is given
        size - property that tracks the number of elements in constant time"""
        self.tree = BinarySearchTree()
        self.size = 0
        if elements is not None:
            for element in elements:
                self.add(element)

    def length(self):
        return self.size

    def contains(self, element):
        """return a boolean indicating whether element is in this set"""
        if self.tree.contains(element):
            return True
        # return self.tree.contains(element) is not None
        return False

    def add(self, element):
        """add element to this set, if not present already"""
        if self.contains(element):
            raise ValueError('Cannot add element to set again: {}'.format(element))
        else:
            self.tree.insert(element)
            self.size += 1

    def remove(self, element):
        """remove element from this set, if present, or else raise KeyError"""
        self.tree.delete(element)
        self.size -= 1

    def union(self, other_set):
        """return a new set that is the union of this set and other_set"""
        union_set = self.tree.items_in_order()
        for element in other_set.tree.items_in_order():
            if element not in union_set:
                union_set.append(element)

        return TreeSet(union_set)

    def intersection(self, other_set):
        """return a new set that is the intersection of this set and other_set"""
        intersection_set = TreeSet()
        for element in self.tree.items_in_order():
            if other_set.contains(element):
                intersection_set.add(element)

        return intersection_set

    def difference(self, other_set):
        """return a new set that is the difference of this set and other_set"""
        difference_set = TreeSet()
        for element in other_set.tree.items_in_order():
            if self.tree.contains(element) is False:
                difference_set.add(element)

        for element in self.tree.items_in_order():
            if other_set.contains(element) is False:
                difference_set.add(element)


        return difference_set

    def is_subset(self, other_set):
        """return a boolean indicating whether this set is a subset of other_set"""
        if other_set.size < self.size:
            return False

        for element in self.tree.items_in_order():
            if other_set.contains(element) is False:
                return False

        return True
Example #23
0
 def test_items_level_order_with_3_strings(self):
     # Create a complete binary search tree of 3 strings in level-order
     items = ['B', 'A', 'C']
     tree = BinarySearchTree(items)
     # Ensure the level-order traversal of tree items is ordered correctly
     assert tree.items_level_order() == ['B', 'A', 'C']
Example #24
0
 def __init__(self, it=()):
     self.data = BinarySearchTree(it)
Example #25
0
 def test_init(self):
     tree = BinarySearchTree()
     assert tree.root is None
     assert tree.size == 0
     assert tree.is_empty() is True
Example #26
0
def tree_sort(items):
    tree = BT(items)
    sorted_items = tree.items_in_order()
    return sorted_items
Example #27
0
class Set_Tree(object):
    def __init__(self, elements=None):
        self.tree = BinarySearchTree()
        self.size = 0
        if elements is not None:
            for element in elements:
                self.add(element)

    def __repr__(self):
        """Return a string representation of this hash table."""
        return 'Set_Tree({!r})'.format(self.tree.items_in_order())

    def __iter__(self):
        for item in self.tree.items_in_order():
            yield item

    def contains(self, element):
        '''
        Return true if this set contains the given element, False otherwise
        Time complexity: O(logn)
        Space complexity: O(logn) if recursive, O(1) if iterative
        '''
        return self.tree.contains(element)

    def add(self, element):
        '''
        Adds element to a set if it's not already in the set
        Time complexity: O(logn).O(logn)
        Space complexity: O(1)
        '''
        #if the element is not already in the set, insert it
        if not self.contains(element):  #O(logn)
            self.tree.insert(element)  #O(logn)
            self.size += 1

    def remove(self, element):
        '''
        Removes the element from the set, if it exists
        Time complexity: O(n) + O(logn) = O(n)
        Space complexity: O(n) getting the inorder item to find successor
        '''
        #delete from the set, if it's in the set
        #else raise key error
        if self.contains(element):  #O(1)
            self.tree.delete(element)  #O(n)
            self.size -= 1
        else:
            raise KeyError

    def union(self, other_set):
        '''
        Return a new set that is the union of this set and other_set
        Time complexity: O(mlogm) + O(nlogn)
        Space complexity: O(m+n)
        '''
        #create a new set with items of self
        new_set = Set_Tree(self)  #O(mlogm)
        #then add everything from other_set to new_set
        for element in other_set:  #O(n)
            new_set.add(element)  #O(logn)
        return new_set

    def intersection(self, other_set):
        '''
        Return a new set that is the intersection of this set and other_set
        Time complexity: O(mlogn).O(mlog(min(m,n)))
        Space complexity: O(log(min(m,n)))
        '''
        #create new set
        new_set = Set_Tree()  #O(1)
        ##########check to see which one is smaller####
        #for item in set1
        #if it's in set2, add it to the new_set
        for item in self:  #O(m)
            if other_set.contains(item):  #O(logn)
                new_set.add(item)  #O(log(min(m,n)))
        return new_set

    def difference(self, other_set):
        '''
        Return a new set that is the difference of this set and other_set
        Time complexity: O(mlog(mn))
        Space complexity: O(log(min(m,n)))
        '''
        #create a new set
        new_set = Set_Tree()
        #if item is in set1 but not in set2, add it
        for item in self:  #O(m)
            if not other_set.contains(
                    item):  #O(logn) #if other set doesn't contain item O(logn)
                new_set.add(item)  #O(1)
        return new_set

    def is_subset(self, other_set):
        '''
        Return a boolean indicating whether other_set is a subset of this set
        Time complexity: O(nlogm)
        Space complexity: O(n)
        '''
        if other_set.size > self.size:  #it can't be a subset if it has more items
            return False
        for element in other_set:  #O(n)
            if not self.contains(element):  #O(logm)
                return False  #not all element of set2 are in set1
        return True  #finished the loop, all elements are in set1
Example #28
0
class TreeSet:
    def __init__(self, items=None):
        self.tree = BinarySearchTree()
        if items is not None:
            for item in items:
                self.add(item)

    def __len__(self):
        return len(self.tree)

    def __contains__(self, item):
        return item in self.tree

    def __iter__(self):
        for item in self.tree.items_in_order():
            yield item

    def add(self, item):
        if item not in self.tree:
            self.tree.insert(item)

    def remove(self, item):
        self.tree.delete(item)

    def _smaller_larger_pair(self, tree_1, tree_2):
        if len(tree_1) < len(tree_2):
            return tree_1, tree_2

        return tree_2, tree_1

    def intersection(self, other_set):
        new_set = TreeSet()
        smaller, larger = self._smaller_larger_pair(self.tree, other_set.tree)
        for item in smaller.items_pre_order():
            if item in larger:
                new_set.add(item)

        return new_set

    def _alternating_generator(self, tree1, tree2):
        items_1 = tree1.items_pre_order()
        items_2 = tree2.items_pre_order()
        for item_1, item_2 in zip(items_1, items_2):
            yield item_1
            yield item_2

    def union(self, other_set):
        new_set = TreeSet()
        for item in self._alternating_generator(self.tree, other_set.tree):
            new_set.add(item)

        return new_set

    def difference(self, other_set):
        new_set = TreeSet()
        for item in self.tree.items_pre_order():
            if item not in other_set:
                new_set.add(item)

        return new_set

    def is_subset(self, other_set):
        if len(self) > len(other_set):
            return False

        for item in self.tree.items_pre_order():
            if not other_set.contains(item):
                return False

        return True
 def __init__(self, elements=None):
     self.tree = BinarySearchTree()
     self.element = BinaryTreeNode
     if elements is not None:
         for element in elements:
             self.add(element)
Example #30
0
    def test_size(self):
        items = [8, 4, 12, 2, 6, 10, 14]
        tree = BinarySearchTree(items)
        assert tree.size == 7

        # insert 1, 3, 5, 7, 9, 11, 13, 15
        tree.insert(1)
        assert tree.size == 8
        tree.insert(3)
        assert tree.size == 9
        tree.insert(5)
        assert tree.size == 10
        tree.insert(7)
        assert tree.size == 11
        tree.insert(9)
        assert tree.size == 12
        tree.insert(11)
        assert tree.size == 13
        tree.insert(13)
        assert tree.size == 14
        tree.insert(15)
        assert tree.size == 15