def tree_sort(items): tree = BinarySearchTree() print(items) for item in items: tree.insert(item) sorted_items = tree.items_in_order() print(sorted_items)
def test_delete(self): items = [10, 5, 15, 3, 8, 7, 9] tree = BinarySearchTree() for item in items: tree.insert(item) tree.delete(10)
def test_find_successor(self): items = [18, 1, 53, 40, 63] tree = BinarySearchTree(items) node = tree._find_node_recursive(53) assert tree._find_successor(node).data == 63 tree.insert(57) tree.insert(98) assert tree._find_successor(node).data == 57
def tree_sort(items): '''Running time best and worst case running time: O(n log n).''' tree = BinarySearchTree() for i in items: tree.insert(i) return tree.items_in_order
def read_route_costs(path): # routes_cost_dict = {} binary_search_tree = BinarySearchTree() for line in open(path, 'r'): route, cost = line.split(',') # routes_cost_dict[route] = cost.strip() pair = (route, cost) binary_search_tree.insert(pair) return binary_search_tree
class Set: """Implements a set using BinarySearchTree""" __slots__ = ("data", ) def __init__(self, it=()): self.data = BinarySearchTree(it) def add(self, elm): self.data.insert(elm) def remove(self, elm): self.data.delete(elm) def union(self, other_set): bigger_set = max(self, other_set, key=lambda set: set.size) smaller_set = min(self, other_set, key=lambda set: set.size) return Set(self.items() + other_set.items()) def intersection(self, other_set): bigger_set = max(self, other_set, key=lambda set: set.size) smaller_set = min(self, other_set, key=lambda set: set.size) return Set( tuple(elm for elm in smaller_set.items() if bigger_set.contains(elm))) def difference(self, other_set): res = Set() for elm in self.items(): if not other_set.contains(elm): res.add(elm) return res def is_subset(self, other_set): for elm in self.items(): if not other_set.contains(elm): return False return True def items(self): return self.data.items_in_order() def contains(self, elm): return self.data.contains(elm) @property def size(self): return self.data.size def __eq__(self, other): if isinstance(other, BinarySearchTree): return self.data == other elif isinstance(other, Set): return self.data == other.data else: raise TypeError("Can only compare with BinarySearchTree and Set")
def get_routes_data(path): '''Reads the file at the given path and store the routes data in a binary tree''' binary_tree = BinarySearchTree() for line in open(path, 'r'): # Open file and read each line route, cost = line.split( ',') # Split each line in route and cost for that route # We need strings because the routes should be ordered alphabetically, not on numerical value match_node = MatchNode(route, cost) # Store the route and cost as a pair binary_tree.insert(match_node) # Insert route, cost pair into tree return binary_tree
def read_cost_file(self, file_name): opened_file = open('data/{}'.format(file_name)) file_lines = opened_file.read().splitlines() binary_search_tree = BinarySearchTree() for line in file_lines: prefix, cost = line.split(',') item = (prefix, cost) binary_search_tree.insert(item) print(binary_search_tree) return binary_search_tree
class CallRouter(object): def __init__(self, phone_numbers_unparsed, route_numbers_unparsed): self.phone_numbers_parsed = self.parse_phone_numbers( phone_numbers_unparsed) self.route_numbers_parsed = self.parse_route_numbers( route_numbers_unparsed) # Data Structure: Binary Search Tree of Prefixes (quick search) self.prefixes = BinarySearchTree() # Data Structure: Dictionary of prefixes and best prices (quick price fetching and price updating) self.best_prices = {} # Step 1: Turn import files into useable python objects def parse_phone_numbers(self, phone_numbers_unparsed): pass def parse_route_numbers(self, route_numbers_unparsed): # Take each line in raw data, deconstruct for entry in route_numbers_unparsed: clean_route = entry(',') prefix = clean_route[0] price = clean_route[1] # If not in prefix binary tree, add to binary tree and add to dictionary if self.prefixes.contains(prefix) == False: self.prefixes.insert(prefix) self.best_prices[prefix] = price # If already in prefix binary tree, update compare and update dictionary else: # If stored price is larger than new price if self.best_prices[prefix] > price: self.best_prices[prefix] = price # Step 2: Find the longest matching prefix (using recursion) def routing_cost(self, phone_number): # Find the longest prefix that matches phone number # Note: We are always storing best prices in the parse route numbers method if phone_number is not None: if self.prefixes.contains(phone_number): return self.best_prices[phone_number] else: index_to_slice = len(phone_number) - 1 return self.routing_cost(phone_number[:index_to_slice]) else: return 0 # Step 3: Record best routing costs results into a readable format def record_routing_costs(self, phone_numbers_parsed): results = [] for phone_number in phone_numbers_parsed: cost = self.routing_cost(phone_number) line_item = "{},{}".format(phone_number, cost) results.append(line_item) print(results)
def test_insert_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() for item in items: tree.insert(item) assert tree.root.data == 4 assert tree.root.left.data == 2 assert tree.root.right.data == 6 assert tree.root.left.left.data == 1 assert tree.root.left.right.data == 3 assert tree.root.right.left.data == 5 assert tree.root.right.right.data == 7
def _map_route_costs(): """create a tree to store the prefix and cost of the routes cost""" with open(_path_for(file_of_costs)) as f_costs: # list of "+123456,0.02" arr_routes = f_costs.read().splitlines() tree_costs = BinarySearchTree() for a_route in arr_routes: # create a node with prefix, cost key-value pair a_route_key, a_route_cost = a_route.split(',') node_cost = CostNode(a_route_key, a_route_cost) tree_costs.insert(node_cost) return tree_costs
def test_delete(self): tree = BinarySearchTree([2, 4]) assert tree.height() == 1 tree.delete(4) assert tree.height() == 0 tree.delete(2) assert tree.height() == 0 items = [18, 1, 53, 40, 63] tree.insert(10) tree = BinarySearchTree(items) tree.delete(1) assert tree.search(1) is None tree.delete(10) assert tree.search(10) is None tree.insert(57) tree.insert(98) tree.delete(63) assert tree.search(63) is None
class TreeSet(AbstractSet): 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) @property def size(self): """ Makes size an attribute """ return self.tree.size def __repr__(self): """Return a string representation of this binary tree node.""" return 'Set({!r})'.format(self.tree.items_in_order()) def contains(self, item): """ Checks if item is in the set Time Complexity: O(logN) only grows in relation to the #node vs height starts fast/heavy then peeks with a lot for a long time""" return self.tree.contains(item) def add(self, item): """ Insert one item to the set if it doesn't already exist Time complexity: O(logN) based off contains then just O(1) to add""" if not self.contains(item): self.tree.insert(item) def remove(self, item): """ Check if item exists and remove it or raise Keyerror Time complexity: O() """ if self.contains(item): self.tree.delete(item) else: raise ValueError(item, "Is not in this set") def items(self): return self.tree.items_in_order()
def test_size(self): tree = BinarySearchTree() assert tree.size == 0 tree.insert('B') assert tree.size == 1 tree.insert('A') assert tree.size == 2 tree.insert('C') assert tree.size == 3
def test_insert_with_3_items(self): # Create a complete binary search tree of 3 items in level-order tree = BinarySearchTree() tree.insert(2) assert tree.root.data == 2 assert tree.root.left is None assert tree.root.right is None tree.insert(1) assert tree.root.data == 2 assert tree.root.left.data == 1 assert tree.root.right is None tree.insert(3) assert tree.root.data == 2 assert tree.root.left.data == 1 assert tree.root.right.data == 3
def test_height(self): tree = BinarySearchTree([2, 1, 3]) assert tree.height() == 1 tree.insert(4) assert tree.height() == 2
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']
class Setree: def __init__(self, ls=None): self.tree = BinarySearchTree() self.size = 0 if ls is not None: for item in ls: self.add(item) def add(self, item): '''Add an item to the set. O(log(n))''' if self.contains(item) is False: self.tree.insert(item) self._update_size() def contains(self, item): '''Check if set contains item. O(log(n))''' return self.tree.contains(item) def remove(self, item): '''Remove item from set or ValueError O(log(n))''' self.tree.delete(item) self._update_size() def union(self, nset): '''Create a new set with elements of both sets O((m+n)*log(m+n))''' return Setree(self.in_order() + nset.in_order()) def intersection(self, nset): '''Create a new set with elements that are in both sets O(min(m,n)*log(n)*log(m))''' if self.size > nset.size: small = nset big = self else: big = nset small = self nnset = Setree() for item in small.in_order(): if big.contains(item): nnset.add(item) return nnset def is_subset(self, nset): '''Check if each item in one set is in this set O(n*log(n)*log(m))''' if self.size < nset.size: return False items = nset.in_order() for i in items: if self.contains(i) is False: return False return True def difference(self, nset): '''Check for each item in this is not in another set O(m*log(n))''' return Setree( [i for i in self.in_order() if nset.contains(i) is False]) def in_order(self): """returns all of the values in a set in sorted order O(m)""" return self.tree.items_in_order() def _update_size(self): """Updates the size.""" self.size = self.tree.size def bad_balance(self): print('Previous height:') print(self._height()) queue = LinkedQueue() ls = self.in_order() self._binary_sorter(ls, queue.enqueue) self.tree = BinarySearchTree() count = 1 while queue.front() is not None: self.add(ls[queue.dequeue()]) count += 1 print('Balanced height:') print(self._height()) print(count) def _binary_sorter(self, ls, visit, last=None, left=None, right=None): if left is None: left, right = 0, len(ls) - 1 midpoint = left + ((right - left) // 2) if midpoint != last: visit(midpoint) if midpoint != last: self._binary_sorter(ls, visit, midpoint, midpoint + 1, right) if midpoint != last: self._binary_sorter(ls, visit, midpoint, left, midpoint - 1) def _height(self): return self.tree.root.height_f()
def test_one_case(self): tree = BinarySearchTree() node = BinaryTreeNode(3) tree.insert(3) assert tree.size == 1 assert tree._find_parent_node_recursive(3, tree.root) == None
class Set: def __init__(self, items): self.set = BinarySearchTree() self.size = 0 for item in items: self.add(item) def __eq__(self, other): return self.set.items_in_order() == other.set.items_in_order() def contains(self, item): '''Check what items are in the set''' return self.set.contains(item) # Time complexity: O(log n) def add(self, item): '''Add an item to a set''' if self.set.contains(item) != True: self.set.insert(item) self.size += 1 # Time Complexity: O(log n) def remove(self, item): '''Remove item from a set''' return self.set.delete(item) # Time Complexity: O(log n) def union(self, other_set): '''Merges sets together, but removes duplicates''' unionset = Set(self.set.items_in_order() + other_set.set.items_in_order()) return unionset # Time Complexity: O(m + n) def intersection(self, other_set): '''Finds the most common elements in both sets''' # if item is present in self.set and other_set: # return item intersectionitems = [] for item in self.set.items_in_order(): if other_set.contains(item) == True: intersectionitems.append(item) intersectionset = Set(intersectionitems) return intersectionset #check if item is in otherset using contains #if it is add to intersectionitems #create a new set out of intersectionitems and return it def difference(self, other_set): '''Checks what items are present in one set that are not present in the other''' differenceitems = [] for item in self.set.items_in_order(): if other_set.contains(item) == False: differenceitems.append(item) differenceset = Set(differenceitems) return differenceset # Time Complexity: O(m + n) def is_subset(self, other_set): '''Checks if a set is a subset of another set''' for item in self.set.items_in_order(): if other_set.contains(item) == False: return False return True
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 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 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 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 Set: def __init__(self, elements=None): self.tree = BinarySearchTree() self.size = 0 if elements is not None: for elm in elements: self.add(elm) def size(self): return self.tree.size def __iter__(self): return self def contains(self, element): if self.tree.contains(element): return True return False def add(self, element): if self.contains(element): raise ValueError("Cannot add element to Set again") else: self.tree.insert(element) self.size += 1 def remove(self, element): self.tree.delete(element) self.size -= 1 def union(self, other_set): """TODO: Running time: O(n*k), have to visit every node TODO: Memory usage: O(n+k) nodes are stored on stack""" result = self.tree.items_in_order() for elm in other_set.tree.items_in_order(): if elm not in result: result.append(elm) return Set(result) def intersection(self, other_set): """TODO: Running time: O(n), have to visit every node TODO: Memory usage: O(n+k) nodes are stored on stack""" result = Set() for elm in self.tree.items_in_order(): if other_set.contains(elm): result.add(elm) return result def difference(self, other_set): """TODO: Running time: O(n), have to visit every node TODO: Memory usage: O(n+k) nodes are stored on stack""" result = Set() for elm in self.tree.items_in_order(): if not other_set.contains(elm): result.add(elm) for elm in other_set.tree.items_in_order(): if elm in result.tree.items_in_order(): result.remove(elm) return result def is_subset(self, other_set): """TODO: Running time: O(n) worst, O(1 best), have to visit every node TODO: Memory usage: O(n+k) nodes are stored on stack""" if self.size > other_set.size: return False for elm in self.tree.items_in_order(): if not other_set.tree.contains(elm): return False return True
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
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 tree_sort(items): binary_tree = BinarySearchTree() # constant time for item in items: # n iterations binary_tree.insert(item) # O(n) print(binary_tree.items_in_order()) binary_tree.items_in_order()
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
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