def test_items_in_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 in-order traversal of tree items is ordered correctly assert tree.items_in_order() == ['A', 'B', 'C']
def test_items_in_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 in-order traversal of tree items is ordered correctly assert tree.items_in_order() == [1, 2, 3, 4, 5, 6, 7]
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 tree_sort(items): tree = BinarySearchTree(items) items[:] = tree.items_in_order()
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 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 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(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
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): """ 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 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 tree_sort(items): tree = BT(items) sorted_items = tree.items_in_order() return sorted_items
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 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()
from sorting import is_sorted from binarytree import BinarySearchTree items = [5, 8, 2, 0, 11, 4, 3, 9] tree = BinarySearchTree(items) print(tree.items_in_order())