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
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)
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
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])
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']
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']
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(())
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)
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()
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 ]
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
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)
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
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']
def __init__(self, it=()): self.data = BinarySearchTree(it)
def test_init(self): tree = BinarySearchTree() assert tree.root is None assert tree.size == 0 assert tree.is_empty() is True
def tree_sort(items): tree = BT(items) sorted_items = tree.items_in_order() return sorted_items
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
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)
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