diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ccd3ca9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: python +python: + - "2.7" + - "3.5" + + +# command to install dependencies +install: + - pip install -e .[test] + # - pip install coveralls + +# command to run tests +script: + - py.test --cov=. --cover-report term-missing + +# notifications +notifications: + email: false \ No newline at end of file diff --git a/README.MD b/README.MD index eaae067..c57000c 100644 --- a/README.MD +++ b/README.MD @@ -1,32 +1,12 @@ +[![Build Status](https://travis-ci.org/CCallahanIV/data-structures.svg?branch=master)](https://travis-ci.org/CCallahanIV/data-structures) #Summary -The assignment was to implement a [Weighted Graph](https://codefellows.github.io/sea-python-401d5/assignments/graph_3_weighted.html) -in Python containing the following methods: - - * g.nodes(): return a list of all nodes in the graph - * g.edges(): return a list of all edges in the graph - * g.add_node(n): adds a new node 'n' to the graph - * g.add_edge(n1, n2): adds a new edge to the graph connecting 'n1' and 'n2', if either n1 or n2 are not already present in the graph, they should be added. - * g.del_node(n): deletes the node 'n' from the graph, raises an error if no such node exists - * g.del_edge(n1, n2): deletes the edge connecting 'n1' and 'n2' from the graph, raises an error if no such edge exists - * g.has_node(n): True if node 'n' is contained in the graph, False if not. - * g.neighbors(n): returns the list of all nodes connected to 'n' by edges, raises an error if n is not in g - * g.adjacent(n1, n2): returns True if there is an edge connecting n1 and n2, False if not, raises an error if either of the supplied nodes are not in g - * g.depth_first_traversal(start): Returns the path list for the entire graph with a depth first traversal. - * g.breadth_first_travers(start): Returns the path list for the entire graph with a breadth first traversal. - - -# Coverage: - ----------- coverage: platform darwin, python 3.5.2-final-0 ----------- - - -| Name | Stmts | Miss | Cover | -| ----------------------- | ----- | ---- | ----- | -| weighted_graph.py | 78 | 3 | 96% | -| test_weighted_graph.py | 178 | 0 | 100% | -| ----------------------- | --- | -- | ---- | -| TOTAL | 256 | 3 | 98% | + insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. + contains(self, string): will return True if the string is in the trie, False if not. + size(self): will return the total number of words contained within the trie. 0 if empty. + remove(self, string): will remove the given string from the trie. If the word does not exist, will raise an appropriate exception. + traversal(self, start=None): a generator yielding values in the tree in depth-first order. + diff --git a/src/balanced_bst.py b/src/balanced_bst.py new file mode 100644 index 0000000..a3fe063 --- /dev/null +++ b/src/balanced_bst.py @@ -0,0 +1,608 @@ +"""Module for Binary Search Tree.""" + +from queue_ds import Queue +import timeit +import random + + +class Node(object): + """Node class.""" + + def __init__(self, value=None, left=None, right=None): + """Init of the Node class.""" + self.value = value + self.left = left + self.right = right + self.parent = None + + +class BinarySearchTree(object): + """Binary Search Tree.""" + + """insert(self, val): will insert the value val into the BST. If val is already present, it will be ignored.""" + """search(self, val): will return the node containing that value, else None""" + """size(self): will return the integer size of the BST (equal to the total number of values stored in the tree). It will return 0 if the tree is empty.""" + """depth(self): will return an integer representing the total number of levels in the tree. If there is one value, the depth should be 1, if two values it will be 2, if three values it may be 2 or three, depending, etc.""" + """contains(self, val): will return True if val is in the BST, False if not.""" + """balance(self): will return an integer, positive or negative that represents how well balanced the tree is. Trees which are higher on the left than the right should return a positive value, trees which are higher on the right than the left should return a negative value. An ideally-balanced tree should return 0.""" + """in_order(self): will return a generator that will return the values in the tree using in-order traversal, one at a time.""" + """pre_order(self): will return a generator that will return the values in the tree using pre-order traversal, one at a time.""" + """post_order(self): will return a generator that will return the values in the tree using post_order traversal, one at a time.""" + """breadth_first(self): will return a generator that will return the values in the tree using breadth-first traversal, one at a time.""" + + def __init__(self, if_iter=None): + """Init of the Binary Search Tree class.""" + self.root = None + self.counter = 0 + if if_iter: + try: + for value in if_iter: + self.insert(value) + except TypeError: + self.insert(if_iter) + self._in_order = self._in_order_trav() + self._pre_order = self._pre_order_trav() + self._post_order = self._post_order_trav() + self._breadth_first = self._breadth_first_trav() + + def size(self): + """Return size of Binary Search Tree.""" + return self.counter + + def contains(self, val): + """Return True if val is in the BST, False if not.""" + if self.search(val): + return True + return False + + def search(self, val): + """Return the node containing that value, else None.""" + vertex = self.root + while vertex: + if val > vertex.value: + if not vertex.right: + return None + vertex = vertex.right + elif val < vertex.value: + if not vertex.left: + return None + vertex = vertex.left + else: + return vertex + return None + + def depth(self): + """ + Return an integer representing the total number of levels in the tree. + + If there is one value, the depth should be 1, if two values it will be 2, + if three values it may be 2 or three, depending, etc. + """ + return self._calc_depth(self.root) + + def _calc_depth(self, tree): + """Calculate the depth of the binary search tree recursively.""" + if tree is None: + return 0 + else: + return max(self._calc_depth(tree.right), self._calc_depth(tree.left)) + 1 + + def balance(self): + """ + Return an integer, positive or negative that represents how well balanced the tree is. + + Trees which are higher on the left than the right should return a positive value, + trees which are higher on the right than the left should return a negative value. + An ideally-balanced tree should return 0. + """ + if self.root is None: + return 0 + return self._calc_balance(self.root) + + def _calc_balance(self, node): + """Calculate the balance of a subtree at node.""" + return self._calc_depth(node.right) - self._calc_depth(node.left) + + def in_order(self): + """Return the next value from the generator _in_order.""" + return next(self._in_order) + + def _in_order_trav(self): + """Traverse in_order, yielding via generator.""" + vertex = self.root + visited = [] + while (visited or vertex is not None): + + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + yield vertex.value + vertex = vertex.right + + def pre_order(self): + """Return the next value from the generator _pre_order.""" + return next(self._pre_order) + + def _pre_order_trav(self): + """Traverse pre_order, yielding via generator.""" + vertex = self.root + visited = [] + while (visited or vertex is not None): + if vertex is not None: + yield vertex.value + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + vertex = vertex.right + + def post_order(self): + """Return the next value from the generator _post_order.""" + return next(self._post_order) + + def _post_order_trav(self): + """Traverse pre_order, yielding via generator.""" + vertex = self.root + peek_vertex = None + last_vertex = None + visited = [] + while (visited or vertex is not None): + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + peek_vertex = visited[-1] + if peek_vertex.right and peek_vertex.right is not last_vertex: + vertex = peek_vertex.right + else: + yield peek_vertex.value + last_vertex = visited.pop() + + def breadth_first(self): + """Return the next value from the generator _breadth_first.""" + return next(self._breadth_first) + + def _breadth_first_trav(self): + """Traverse breadth first order, yielding a generator.""" + q = Queue() + q.enqueue(self.root) + while len(q) > 0: + vertex = q.dequeue() + yield vertex.value + if (vertex.left): + q.enqueue(vertex.left) + if (vertex.right): + q.enqueue(vertex.right) + + def insert(self, val): + """Take a value, inserts into Binary Search Tree at correct placement.""" + if self.root is None: + self.root = Node(val) + self.counter += 1 + + else: + vertex = self.root + while True: + if val > vertex.value: + if vertex.right: + vertex = vertex.right + else: + new_node = Node(val) + vertex.right = new_node + new_node.parent = vertex + self.counter += 1 + break + + elif val < vertex.value: + if vertex.left: + vertex = vertex.left + else: + new_node = Node(val) + vertex.left = new_node + new_node.parent = vertex + self.counter += 1 + break + else: + break + self._balance_tree() + + def delete(self, val): + """Remove val from the tree if present, if not present this method is a no-op. Return None in all cases.""" + vertex = self.root + parent_of_del = None + del_node = None + if self.root is None: + return + if self.root.value == val: + left = self.root.left + right = self.root.right + if not right: + self.root = self.root.left + self.root.parent = None + self.counter -= 1 + self._balance_tree() + return + if not right.left: + self.root = right + self.root.left = left + self.root.parent = None + self.counter -= 1 + self._balance_tree() + return + vertex = vertex.right + while True: + if not vertex.left.left: + min_parent = vertex + break + else: + vertex = vertex.left + min_node = min_parent.left + min_parent.left = min_node.right + if min_parent.left: + min_parent.left.parent = min_parent + self.root = min_node + self.root.left = left + self.root.right = right + self.root.parent = None + self.counter -= 1 + self._balance_tree() + else: + while True: + if vertex.right and val == vertex.right.value: + parent_of_del = vertex + del_node = parent_of_del.right + min_parent = self._find_min_parent(parent_of_del, "right") + break + elif vertex.left and val == vertex.left.value: + parent_of_del = vertex + del_node = parent_of_del.left + min_parent = self._find_min_parent(parent_of_del, "left") + break + elif val > vertex.value and vertex.right: + vertex = vertex.right + elif val < vertex.value and vertex.left: + vertex = vertex.left + else: + self._balance_tree() + return + + if parent_of_del.right and val == parent_of_del.right.value: + if not min_parent: + parent_of_del.right = None + self.counter -= 1 + self._balance_tree() + return + if min_parent is del_node: + right = del_node.right + del_node_left = del_node.left + parent_of_del.right = right + if right: + right.parent = parent_of_del + parent_of_del.right.left = del_node_left + if del_node_left: + del_node_left.parent = parent_of_del.right + self.counter -= 1 + self._balance_tree() + return + left = del_node.left + right = del_node.right + min_node = min_parent.left + min_parent.left = min_node.right + if min_parent.left: + min_parent.left.parent = min_parent + del_node = min_node + del_node.right = right + if right: + del_node.right.parent = del_node + del_node.left = left + if left: + del_node.left.parent = del_node + parent_of_del.right = del_node + if del_node: + parent_of_del.right.parent = parent_of_del + self.counter -= 1 + self._balance_tree() + + elif parent_of_del.left and val == parent_of_del.left.value: + if not min_parent: + parent_of_del.left = None + self.counter -= 1 + self._balance_tree() + return + if min_parent is del_node: + left = del_node.right + del_node_left = del_node.left + parent_of_del.left = left + if left: + left.parent = parent_of_del + parent_of_del.left.left = del_node_left + if del_node_left: + del_node_left.parent = parent_of_del.right + self.counter -= 1 + self._balance_tree() + return + left = del_node.left + right = del_node.right + min_node = min_parent.left + min_parent.left = min_node.right + if min_parent.left: + min_parent.left.parent = min_parent + del_node = min_node + del_node.right = right + if right: + del_node.right.parent = del_node + del_node.left = left + if left: + del_node.left.parent = del_node + parent_of_del.left = del_node + if del_node: + parent_of_del.left.parent = parent_of_del + self.counter -= 1 + self._balance_tree() + + def _find_min_parent(self, vertex, side): + """Find the parent of the replacement node, given the parent of the delete node.""" + if side == "right": + if not vertex.right.right and not vertex.right.left: + return + if vertex.right.right and not vertex.right.right.left: + return vertex.right + elif vertex.right.right and vertex.right.right.left: + vertex = vertex.right.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left + else: + if not vertex.left.right and not vertex.left.left: + return + if vertex.left.right and not vertex.left.right.left: + return vertex.left + elif vertex.left.right and vertex.left.right.left: + vertex = vertex.left.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left + return + + def _balance_tree(self): + for node in self._post_order_node(): + if self._calc_balance(node) > 1: + if self._calc_balance(node.right) < 0: + self._right_left_rotation(node) + else: + self._left_rotation(node) + elif self._calc_balance(node) < -1: + if self._calc_balance(node.left) > 0: + self._left_right_rotation(node) + else: + self._right_rotation(node) + + def _left_rotation(self, node): + a = node + a_parent = a.parent + b = a.right + d = b.left + if a is self.root: + self.root = b + self.root.parent = None + self.root.left = a + a.parent = self.root + a.right = d + if d: + d.parent = a + return + a_parent.right = b + b.parent = a_parent + b.left = a + a.parent = b + a.right = d + if d: + d.parent = a + + def _right_rotation(self, node): + a = node + a_parent = a.parent + b = a.left + d = b.right + if a is self.root: + self.root = b + self.root.parent = None + self.root.right = a + a.parent = self.root + a.left = d + if d: + d.parent = a + return + a_parent.left = b + b.parent = a_parent + b.right = a + a.parent = b + a.left = d + if d: + d.parent = a + + def _left_right_rotation(self, node): + """Try left right rotation on node.""" + # if self._calc_balance(node.left) > 0: + if node.left.right.left: + vertex = node + left_head = node.left + right_sub = node.left.right + switcher = node.left.right.left + # if node.left.right.left: + # switcher = node.left.right.left + if vertex.parent: + right_sub.parent = vertex.parent + if vertex.parent.value > vertex.value: + vertex.parent.left = right_sub + else: + vertex.parent.right = right_sub + else: + self.root = right_sub + right_sub.parent = None + switcher.parent = left_head + # if node.left.right.left: + # switcher.parent = left_head + left_head.parent = right_sub + left_head.right = switcher + # if node.left.right.left: + # left_head.right = switcher + right_sub.right = vertex + right_sub.left = left_head + vertex.parent = right_sub + vertex.left = None + else: + vertex = node + left_head = node.left + right_sub = node.left.right + switcher = node.left.right.right + # if node.left.right.right: + # switcher = node.left.right.right + if vertex.parent: + right_sub.parent = vertex.parent + if vertex.parent.value > vertex.value: + vertex.parent.left = right_sub + else: + vertex.parent.right = right_sub + else: + self.root = right_sub + right_sub.parent = None + switcher.parent = vertex + vertex.left = switcher + # if node.left.right.right: + # switcher.parent = vertex + # if node.left.right.right: + # vertex.left = switcher + vertex.parent = right_sub + right_sub.right = vertex + right_sub.left = left_head + left_head.parent = right_sub + left_head.right = None + + def _right_left_rotation(self, node): + """Try right left rotation on node.""" + if node.right.left.left: + # if self._calc_balance(node.right) < 0: + vertex = node + right_head = node.right + left_sub = node.right.left + switcher = node.right.left.left + # if node.right.left.left: + # switcher = node.right.left.left + if vertex.parent: + left_sub.parent = vertex.parent + if vertex.parent.value > vertex.value: + vertex.parent.left = left_sub + else: + vertex.parent.right = left_sub + else: + self.root = left_sub + left_sub.parent = None + switcher.parent = right_head + # if node.right.left.left: + # switcher.parent = right_head + right_head.parent = vertex + right_head.left = None + left_sub.right = right_head + left_sub.left = vertex + vertex.parent = left_sub + vertex.right = switcher + # if node.right.left.left: + # vertex.right = switcher + # else: + # vertex.right = None + else: + # import pdb; pdb.set_trace() + vertex = node + right_head = node.right + left_sub = node.right.left + switcher = node.right.left.right + # if node.right.left.right: + # switcher = node.right.left.right + if vertex.parent: + left_sub.parent = vertex.parent + if vertex.parent.value > vertex.value: + vertex.parent.left = left_sub + else: + vertex.parent.right = left_sub + else: + self.root = left_sub + left_sub.parent = None + switcher.parent = right_head + # if node.right.left.right: + # switcher.parent = right_head + vertex.right = None + vertex.parent = left_sub + left_sub.left = vertex + left_sub.right = right_head + right_head.left = switcher + # if node.right.left.right: + # right_head.left = switcher + right_head.parent = left_sub + + def _post_order_node(self): + vertex = self.root + peek_vertex = None + last_vertex = None + visited = [] + while (visited or vertex is not None): + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + peek_vertex = visited[-1] + if peek_vertex.right and peek_vertex.right is not last_vertex: + vertex = peek_vertex.right + else: + yield peek_vertex + last_vertex = visited.pop() + + # def balance(self): + # """ + # Return an integer, positive or negative that represents how well balanced the tree is. + + # Trees which are higher on the left than the right should return a positive value, + # trees which are higher on the right than the left should return a negative value. + # An ideally-balanced tree should return 0. + # """ + # if self.root is None: + # return 0 + # return self._calc_depth(self.root.right) - self._calc_depth(self.root.left) + + + # def print_bst(self): + # """Return.""" + # thislevel = [self.root] + # while thislevel: + # nextlevel = [] + # print_level = [] + # counter = 0 + # for n in thislevel: + # print(print_level[counter]) + # if n.left: + # nextlevel.append(n.left) + # print_level.append(n.left.value) + # else: + # print_level.append('_') + # if n.right: + # nextlevel.append(n.right) + # print_level.append(n.right.value) + # else: + # print_level.append('_') + # print() + # thislevel = nextlevel + + + +# if __name__ == "__main__": + +# res1 = timeit.repeat(stmt="depth(g)", setup="from graph import g, depth", number=10, repeat=3) +# res2 = timeit.repeat(stmt="breadth(g)", setup="from graph import g, breadth", number=10, repeat=3) +# print("Depth First: ", res1) +# print("Breadth First: ", res2) diff --git a/src/bst.py b/src/bst.py new file mode 100644 index 0000000..e8bba13 --- /dev/null +++ b/src/bst.py @@ -0,0 +1,330 @@ +"""Module for Binary Search Tree.""" + +from queue_ds import Queue +import timeit +import random + + +class Node(object): + """Node class.""" + + def __init__(self, value=None, left=None, right=None): + """Init of the Node class.""" + self.value = value + self.left = left + self.right = right + + +class BinarySearchTree(object): + """Binary Search Tree.""" + + """insert(self, val): will insert the value val into the BST. If val is already present, it will be ignored.""" + """search(self, val): will return the node containing that value, else None""" + """size(self): will return the integer size of the BST (equal to the total number of values stored in the tree). It will return 0 if the tree is empty.""" + """depth(self): will return an integer representing the total number of levels in the tree. If there is one value, the depth should be 1, if two values it will be 2, if three values it may be 2 or three, depending, etc.""" + """contains(self, val): will return True if val is in the BST, False if not.""" + """balance(self): will return an integer, positive or negative that represents how well balanced the tree is. Trees which are higher on the left than the right should return a positive value, trees which are higher on the right than the left should return a negative value. An ideally-balanced tree should return 0.""" + """in_order(self): will return a generator that will return the values in the tree using in-order traversal, one at a time.""" + """pre_order(self): will return a generator that will return the values in the tree using pre-order traversal, one at a time.""" + """post_order(self): will return a generator that will return the values in the tree using post_order traversal, one at a time.""" + """breadth_first(self): will return a generator that will return the values in the tree using breadth-first traversal, one at a time.""" + + def __init__(self, if_iter=None): + """Init of the Binary Search Tree class.""" + self.root = None + self.counter = 0 + if if_iter: + try: + for value in if_iter: + self.insert(value) + except TypeError: + self.insert(if_iter) + self._in_order = self._in_order_trav() + self._pre_order = self._pre_order_trav() + self._post_order = self._post_order_trav() + self._breadth_first = self._breadth_first_trav() + + def insert(self, val): + """Take a value, inserts into Binary Search Tree at correct placement.""" + if self.root is None: + self.root = Node(val) + self.counter += 1 + + else: + vertex = self.root + while True: + if val > vertex.value: + if vertex.right: + vertex = vertex.right + else: + vertex.right = Node(val) + self.counter += 1 + break + + elif val < vertex.value: + if vertex.left: + vertex = vertex.left + else: + vertex.left = Node(val) + self.counter += 1 + break + else: + break + + def size(self): + """Return size of Binary Search Tree.""" + return self.counter + + def contains(self, val): + """Return True if val is in the BST, False if not.""" + if self.search(val): + return True + return False + + def search(self, val): + """Return the node containing that value, else None.""" + vertex = self.root + while vertex: + if val > vertex.value: + if not vertex.right: + return None + vertex = vertex.right + elif val < vertex.value: + if not vertex.left: + return None + vertex = vertex.left + else: + return vertex + return None + + def depth(self): + """ + Return an integer representing the total number of levels in the tree. + + If there is one value, the depth should be 1, if two values it will be 2, + if three values it may be 2 or three, depending, etc. + """ + return self._calc_depth(self.root) + + def _calc_depth(self, tree): + """Calculate the depth of the binary search tree recursively.""" + if tree is None: + return 0 + else: + return max(self._calc_depth(tree.right), self._calc_depth(tree.left)) + 1 + + def balance(self): + """ + Return an integer, positive or negative that represents how well balanced the tree is. + + Trees which are higher on the left than the right should return a positive value, + trees which are higher on the right than the left should return a negative value. + An ideally-balanced tree should return 0. + """ + if self.root is None: + return 0 + return self._calc_depth(self.root.right) - self._calc_depth(self.root.left) + + def in_order(self): + """Return the next value from the generator _in_order.""" + return next(self._in_order) + + def _in_order_trav(self): + """Traverse in_order, yielding via generator.""" + vertex = self.root + visited = [] + while (visited or vertex is not None): + + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + yield vertex.value + vertex = vertex.right + + def pre_order(self): + """Return the next value from the generator _pre_order.""" + return next(self._pre_order) + + def _pre_order_trav(self): + """Traverse pre_order, yielding via generator.""" + vertex = self.root + visited = [] + while (visited or vertex is not None): + if vertex is not None: + yield vertex.value + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + vertex = vertex.right + + def post_order(self): + """Return the next value from the generator _post_order.""" + return next(self._post_order) + + def _post_order_trav(self): + """Traverse pre_order, yielding via generator.""" + vertex = self.root + peek_vertex = None + last_vertex = None + visited = [] + while (visited or vertex is not None): + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + peek_vertex = visited[-1] + if peek_vertex.right and peek_vertex.right is not last_vertex: + vertex = peek_vertex.right + else: + yield peek_vertex.value + last_vertex = visited.pop() + + def breadth_first(self): + """Return the next value from the generator _breadth_first.""" + return next(self._breadth_first) + + def _breadth_first_trav(self): + """Traverse breadth first order, yielding a generator.""" + q = Queue() + q.enqueue(self.root) + while len(q) > 0: + vertex = q.dequeue() + yield vertex.value + if (vertex.left): + q.enqueue(vertex.left) + if (vertex.right): + q.enqueue(vertex.right) + + def delete(self, val): + """Remove val from the tree if present, if not present this method is a no-op. Return None in all cases.""" + vertex = self.root + parent_of_del = None + del_node = None + if self.root is None: + return + if self.root.value == val: + left = self.root.left + right = self.root.right + if not right: + self.root = self.root.left + self.counter -= 1 + return + if not right.left: + self.root = right + self.root.left = left + self.counter -= 1 + return + vertex = vertex.right + while True: + if not vertex.left.left: + min_parent = vertex + break + else: + vertex = vertex.left + min_node = min_parent.left + min_parent.left = min_node.right + self.root = min_node + self.root.left = left + self.root.right = right + self.counter -= 1 + else: + while True: + if vertex.right and val == vertex.right.value: + parent_of_del = vertex + del_node = parent_of_del.right + min_parent = self._find_min_parent(parent_of_del, "right") + break + elif vertex.left and val == vertex.left.value: + parent_of_del = vertex + del_node = parent_of_del.left + min_parent = self._find_min_parent(parent_of_del, "left") + break + elif val > vertex.value and vertex.right: + vertex = vertex.right + elif val < vertex.value and vertex.left: + vertex = vertex.left + else: + return + + if parent_of_del.right and val == parent_of_del.right.value: + if not min_parent: + parent_of_del.right = None + self.counter -= 1 + return + if min_parent is del_node: + right = del_node.right + del_node_left = del_node.left + parent_of_del.right = right + parent_of_del.right.left = del_node_left + self.counter -= 1 + return + left = del_node.left + right = del_node.right + min_node = min_parent.left + min_parent.left = min_node.right + del_node = min_node + del_node.right = right + del_node.left = left + parent_of_del.right = del_node + self.counter -= 1 + + elif parent_of_del.left and val == parent_of_del.left.value: + if not min_parent: + parent_of_del.left = None + self.counter -= 1 + return + if min_parent is del_node: + left = del_node.right + del_node_left = del_node.left + parent_of_del.left = left + parent_of_del.left.left = del_node_left + self.counter -= 1 + return + left = del_node.left + right = del_node.right + min_node = min_parent.left + min_parent.left = min_node.right + del_node = min_node + del_node.right = right + del_node.left = left + parent_of_del.left = del_node + self.counter -= 1 + + def _find_min_parent(self, vertex, side): + """Find the parent of the replacement node, given the parent of the delete node.""" + if side == "right": + if not vertex.right.right and not vertex.right.left: + return + if vertex.right.right and not vertex.right.right.left: + return vertex.right + elif vertex.right.right and vertex.right.right.left: + vertex = vertex.right.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left + else: + if not vertex.left.right and not vertex.left.left: + return + if vertex.left.right and not vertex.left.right.left: + return vertex.left + elif vertex.left.right and vertex.left.right.left: + vertex = vertex.left.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left + return + + +# if __name__ == "__main__": + +# res1 = timeit.repeat(stmt="depth(g)", setup="from graph import g, depth", number=10, repeat=3) +# res2 = timeit.repeat(stmt="breadth(g)", setup="from graph import g, breadth", number=10, repeat=3) +# print("Depth First: ", res1) +# print("Breadth First: ", res2) diff --git a/src/hash_table.py b/src/hash_table.py new file mode 100644 index 0000000..d0ac755 --- /dev/null +++ b/src/hash_table.py @@ -0,0 +1,132 @@ +"""Hash table module.""" + +# HASH TABLE (HT) +# +# CodeFellows 401d5 +# Submission Date: +# +# Authors: Colin Lamont +# Ben Shields +# +# URL: + + +""" +The HashTable is a data structure that implements an +assosiative array. The structure maps keys to values +in such a way that values may be accessed in O(1) time. +This is done through use of a hashing function. +The hashing function maps the content of the key to +a specific bucket. As the content of the key always +maps to a specific hash value, the key's value may +be accessed quickly. + + - get(key) - should return the value stored with the given key + - set(key, val) - should store the given val using the given key + - _hash(key) - should hash the key provided (note that this is an internal api) +""" + + +class HashTable(object): + """Hash Table.""" + + """self._num_buckets - the number of buckets in the Hash Table.""" + """get(key) - should return the value stored with the given key.""" + """set(key, val) - store the given val using the given key.""" + """_hash(key) - hash the key provided (note that this is an internal api).""" + + def __init__(self, hash_type='additive'): + """Init function for the Hash Table class.""" + self._num_buckets = 50000 + self._container = [[] for i in range(0, self._num_buckets)] + self._type = hash_type + + def get(self, key): + """Return the value corresponding to this key in the hash table.""" + hashed_value = self._hash(key) + for each in self._container[hashed_value]: + if each[0] == key: + return each[1] + return 'Key not in hash table.' + + def set(self, key, val): + """Place the key value pair in the hash table.""" + if type(key) is not str: + return 'Keys must be strings.' + hashed_value = self._hash(key) + for each in self._container[hashed_value]: + if each[0] == key: + (self._container[hashed_value]).remove(each) + (self._container[hashed_value]).append((key, val)) + + def _hash(self, key): + """Additive hash function.""" + if self._type == 'additive': + return self._additive_hash(key) + if self._type == 'colin-ben': + return self._colin_ben_hash(key) + + def _additive_hash(self, key): + return sum([ord(each) for each in key]) % self._num_buckets + + def _colin_ben_hash(self, key): + ords = [] + sieve = gen_primes() + for each in key: + num = ord(each) + if ord(each) % 2 == 0: + num = int(str(ord(each))[::-1]) + ords.append([num, next(sieve)]) + a_sum = sum([each[0] * each[1] for each in ords]) + return a_sum % self._num_buckets + + # def _colin_ben_hash(self, key): + # ords = [] + # sieve = gen_primes() + # for each in key: + # num = ord(each) + # if ord(each) % 2 == 0: + # num = int(str(ord(each))[::-1]) + # ords.append([num, next(sieve)]) + # a_sum = sum([(each[0] << next(sieve)) * (each[1] << next(sieve)) for each in ords]) + # return a_sum % self._num_buckets + + +def gen_primes(): + """ Generate an infinite sequence of prime numbers. + """ + # Maps composites to primes witnessing their compositeness. + # This is memory efficient, as the sieve is not "run forward" + # indefinitely, but only as long as required by the current + # number being tested. + # + D = {} + + # The running integer that's checked for primeness + q = 2 + + while True: + if q not in D: + # q is a new prime. + # Yield it and mark its first multiple that isn't + # already marked in previous iterations + # + yield q + D[q * q] = [q] + else: + # q is composite. D[q] is the list of primes that + # divide it. Since we've reached q, we no longer + # need it in the map, but we'll mark the next + # multiples of its witnesses to prepare for larger + # numbers + # + for p in D[q]: + D.setdefault(p + q, []).append(p) + del D[q] + + q += 1 + +# Gen Primes function: +# Sieve of Eratosthenes +# Code by David Eppstein, UC Irvine, 28 Feb 2002 +# http://code.activestate.com/recipes/117119/ diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py new file mode 100644 index 0000000..0dfbfe1 --- /dev/null +++ b/src/test_balanced_bst.py @@ -0,0 +1,738 @@ +"""Test Module for Balanced Binary Search Tree.""" +from balanced_bst import BinarySearchTree +import pytest + +BST_ROT_2_R_1 = [3, 2] +BST_ROT_2_L_3 = [1, 2] +BST_ROT_LR_GC_7 = [10, 12, 5, 8, 2] +BST_ROT_RL = [10, 5, 15, 13, 20] + + +@pytest.fixture +def filled_bst_rot_2_r_1(): + """Fixture for a 2 node tree for a right rotation with insertion of 1.""" + new_tree = BinarySearchTree(BST_ROT_2_R_1) + return new_tree + + +@pytest.fixture +def filled_bst_rot_2_l_3(): + """Fixture for a 2 node tree for a left rotation with insertion of 3.""" + new_tree = BinarySearchTree(BST_ROT_2_L_3) + return new_tree + + +@pytest.fixture +def filled_bst_rot_lr_gc_7(): + """Fixture for a binary search tree for a left-right rotation with insertion of 7 to tree 10, 12, 5, 8, 2.""" + new_tree = BinarySearchTree(BST_ROT_LR_GC_7) + return new_tree + + +@pytest.fixture +def filled_bst_rot_rl(): + """Fixture for a binary search tree for a right-left rotation.""" + new_tree = BinarySearchTree(BST_ROT_RL) + return new_tree + + +def test_simple_3_node_right_rotation(filled_bst_rot_2_r_1): + """Balance bst via right rotation, when adding 1 to tree of 3, 2.""" + a = filled_bst_rot_2_r_1 + a.insert(1) + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 + + +def test_simple_3_node_left_rotation(filled_bst_rot_2_l_3): + """Balance bst via left rotation, when adding 3 to tree of 1, 2.""" + a = filled_bst_rot_2_l_3 + a.insert(3) + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 + + +def test_left_right_rotation(filled_bst_rot_lr_gc_7): + """Balance bst via left-right rotation, when adding 7 to tree of 10, 12, 5, 8, 2.""" + a = filled_bst_rot_lr_gc_7 + a.insert(7) + assert a.root.value == 8 + assert a.root.right.value == 10 + assert a.root.left.value == 5 + assert a.root.left.right.value == 7 + assert a.root.left.left.value == 2 + assert a.root.right.right.value == 12 + + +def test_bst_empty_tree(): + """Test balancing empty tree.""" + a = BinarySearchTree() + assert a.root is None + + +def test_bst_1_value(): + """Test balancing tree with one value.""" + a = BinarySearchTree() + a.insert(1) + assert a.root.value == 1 + assert a.root.right is None + assert a.root.left is None + + +def test_bst_2_values(): + """Test balancing tree with two values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + assert a.root.value == 1 + assert a.root.right.value == 2 + assert a.root.left is None + + +def test_bst_3_values(): + """Test balancing tree with three values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 + + +def test_bst_4_values(): + """Test balancing tree with four values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 + assert a.root.right.right.value == 4 + + +def test_bst_5_values(): + """Test balancing tree with five values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + assert a.root.value == 2 + assert a.root.right.value == 4 + assert a.root.left.value == 1 + assert a.root.right.right.value == 5 + assert a.root.right.left.value == 3 + + +def test_bst_6_values(): + """Test balancing tree with six values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + assert a.root.value == 4 + assert a.root.right.value == 5 + assert a.root.left.value == 2 + assert a.root.right.right.value == 6 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + + +def test_bst_7_values(): + """Test balancing tree with seven values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + assert a.root.value == 4 + assert a.root.right.value == 6 + assert a.root.left.value == 2 + assert a.root.right.right.value == 7 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.left.value == 5 + + +def test_bst_8_values(): + """Test balancing tree with eight values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + assert a.root.value == 4 + assert a.root.right.value == 6 + assert a.root.left.value == 2 + assert a.root.right.right.value == 7 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.left.value == 5 + assert a.root.right.right.right.value == 8 + + +def test_bst_9_values(): + """Test balancing tree with nine values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + assert a.root.value == 4 + assert a.root.right.value == 6 + assert a.root.left.value == 2 + assert a.root.right.right.value == 8 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.left.value == 5 + assert a.root.right.right.right.value == 9 + assert a.root.right.right.left.value == 7 + + +def test_bst_10_values(): + """Test balancing tree with ten values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + assert a.root.value == 4 + assert a.root.right.value == 8 + assert a.root.left.value == 2 + assert a.root.right.right.value == 9 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.right.right.value == 10 + assert a.root.right.left.value == 6 + assert a.root.right.left.right.value == 7 + assert a.root.right.left.left.value == 5 + + +def test_bst_11_values(): + """Test balancing tree with eleven values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + a.insert(11) + assert a.root.value == 4 + assert a.root.right.value == 8 + assert a.root.left.value == 2 + assert a.root.right.right.value == 10 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.right.right.value == 11 + assert a.root.right.right.left.value == 9 + assert a.root.right.left.value == 6 + assert a.root.right.left.right.value == 7 + assert a.root.right.left.left.value == 5 + + +def test_bst_12_values(): + """Test balancing tree with twelve values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + a.insert(11) + a.insert(12) + assert a.root.value == 8 + assert a.root.right.value == 10 + assert a.root.right.right.value == 11 + assert a.root.right.left.value == 9 + assert a.root.right.right.right.value == 12 + assert a.root.left.value == 4 + assert a.root.left.left.value == 2 + assert a.root.left.left.left.value == 1 + assert a.root.left.left.right.value == 3 + assert a.root.left.right.value == 6 + assert a.root.left.right.left.value == 5 + assert a.root.left.right.right.value == 7 + + +def test_bst_13_values(): + """Test balancing tree with thirteen values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + a.insert(11) + a.insert(12) + a.insert(13) + assert a.root.value == 8 + assert a.root.right.value == 10 + assert a.root.right.right.value == 12 + assert a.root.right.left.value == 9 + assert a.root.right.right.right.value == 13 + assert a.root.right.right.left.value == 11 + assert a.root.left.value == 4 + assert a.root.left.left.value == 2 + assert a.root.left.left.left.value == 1 + assert a.root.left.left.right.value == 3 + assert a.root.left.right.value == 6 + assert a.root.left.right.left.value == 5 + assert a.root.left.right.right.value == 7 + + +def test_bst_14_values(): + """Test balancing tree with fourteen values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + a.insert(11) + a.insert(12) + a.insert(13) + a.insert(14) + assert a.root.value == 8 + assert a.root.right.value == 12 + assert a.root.right.right.value == 13 + assert a.root.right.left.value == 10 + assert a.root.right.right.right.value == 14 + assert a.root.right.left.left.value == 9 + assert a.root.right.left.right.value == 11 + assert a.root.left.value == 4 + assert a.root.left.left.value == 2 + assert a.root.left.left.left.value == 1 + assert a.root.left.left.right.value == 3 + assert a.root.left.right.value == 6 + assert a.root.left.right.left.value == 5 + assert a.root.left.right.right.value == 7 + + +def test_bst_1_value_starting_at_14_down(): + """Test balancing tree with one value.""" + a = BinarySearchTree() + a.insert(14) + assert a.root.value == 14 + assert a.root.right is None + assert a.root.left is None + + +def test_bst_2_values_starting_at_14_down(): + """Test balancing tree with two values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + assert a.root.value == 14 + assert a.root.right is None + assert a.root.left.value == 13 + + +def test_bst_3_values_starting_at_14_down(): + """Test balancing tree with three values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + assert a.root.value == 13 + assert a.root.right.value == 14 + assert a.root.left.value == 12 + + +def test_bst_4_values_starting_at_14_down(): + """Test balancing tree with four values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + assert a.root.value == 13 + assert a.root.right.value == 14 + assert a.root.left.value == 12 + assert a.root.left.left.value == 11 + + +def test_bst_5_values_starting_at_14_down(): + """Test balancing tree with five values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + assert a.root.value == 13 + assert a.root.right.value == 14 + assert a.root.left.value == 11 + assert a.root.left.right.value == 12 + assert a.root.left.left.value == 10 + + +def test_bst_6_values_starting_at_14_down(): + """Test balancing tree with six values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.left.value == 10 + assert a.root.right.right.value == 14 + assert a.root.right.left.value == 12 + assert a.root.left.left.value == 9 + + +def test_bst_7_values_starting_at_14_down(): + """Test balancing tree with seven values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.left.value == 9 + assert a.root.right.right.value == 14 + assert a.root.left.left.value == 8 + assert a.root.left.right.value == 10 + assert a.root.right.left.value == 12 + + +def test_bst_8_values_starting_at_14_down(): + """Test balancing tree with eight values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.left.value == 9 + assert a.root.right.right.value == 14 + assert a.root.left.left.value == 8 + assert a.root.left.right.value == 10 + assert a.root.right.left.value == 12 + assert a.root.left.left.left.value == 7 + + +def test_bst_9_values_starting_at_14_down(): + """Test balancing tree with nine values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.left.value == 9 + assert a.root.right.right.value == 14 + assert a.root.left.left.value == 7 + assert a.root.left.right.value == 10 + assert a.root.right.left.value == 12 + assert a.root.left.left.left.value == 6 + assert a.root.left.left.right.value == 8 + + +def test_bst_10_values_starting_at_14_down(): + """Test balancing tree with ten values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.right.right.value == 14 + assert a.root.right.left.value == 12 + assert a.root.left.value == 7 + assert a.root.left.left.value == 6 + assert a.root.left.right.value == 9 + assert a.root.left.left.left.value == 5 + assert a.root.left.right.right.value == 10 + assert a.root.left.right.left.value == 8 + + +def test_bst_11_values_starting_at_14_down(): + """Test balancing tree with eleven values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + a.insert(4) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.right.right.value == 14 + assert a.root.right.left.value == 12 + assert a.root.left.value == 7 + assert a.root.left.left.value == 5 + assert a.root.left.right.value == 9 + assert a.root.left.left.left.value == 4 + assert a.root.left.right.right.value == 10 + assert a.root.left.right.left.value == 8 + assert a.root.left.left.right.value == 6 + + +def test_bst_12_values_starting_at_14_down(): + """Test balancing tree with twelve values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + a.insert(4) + a.insert(3) + assert a.root.value == 7 + assert a.root.right.value == 11 + assert a.root.right.right.value == 13 + assert a.root.right.left.value == 9 + assert a.root.right.left.left.value == 8 + assert a.root.right.left.right.value == 10 + assert a.root.right.right.left.value == 12 + assert a.root.right.right.right.value == 14 + assert a.root.left.value == 5 + assert a.root.left.left.value == 4 + assert a.root.left.right.value == 6 + assert a.root.left.left.left.value == 3 + + +def test_bst_13_values_starting_at_14_down(): + """Test balancing tree with thirteen values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + a.insert(4) + a.insert(3) + a.insert(2) + assert a.root.value == 7 + assert a.root.right.value == 11 + assert a.root.right.right.value == 13 + assert a.root.right.left.value == 9 + assert a.root.right.left.left.value == 8 + assert a.root.right.left.right.value == 10 + assert a.root.right.right.left.value == 12 + assert a.root.right.right.right.value == 14 + assert a.root.left.value == 5 + assert a.root.left.left.value == 3 + assert a.root.left.right.value == 6 + assert a.root.left.left.left.value == 2 + assert a.root.left.left.right.value == 4 + + +def test_bst_14_values_starting_at_14_down(): + """Test balancing tree with fourteen values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + a.insert(4) + a.insert(3) + a.insert(2) + a.insert(1) + assert a.root.value == 7 + assert a.root.right.value == 11 + assert a.root.right.right.value == 13 + assert a.root.right.left.value == 9 + assert a.root.right.left.left.value == 8 + assert a.root.right.left.right.value == 10 + assert a.root.right.right.left.value == 12 + assert a.root.right.right.right.value == 14 + assert a.root.left.value == 3 + assert a.root.left.left.value == 2 + assert a.root.left.right.value == 5 + assert a.root.left.left.left.value == 1 + assert a.root.left.right.right.value == 6 + assert a.root.left.right.left.value == 4 + + +def test_bst_left_right_basic(filled_bst_rot_2_r_1): + """Test left right rotation.""" + a = filled_bst_rot_2_r_1 + a.insert(1) + assert a.root.value == 2 + assert a.root.parent is None + assert a.root.left.value == 1 + assert a.root.right.value == 3 + + +def test_bst_right_left_basic(filled_bst_rot_2_l_3): + """Test right left rotation.""" + a = filled_bst_rot_2_l_3 + a.insert(3) + assert a.root.value == 2 + assert a.root.parent is None + assert a.root.left.value == 1 + assert a.root.right.value == 3 + + +def test_bst_left_right_root_left_right_add_left(filled_bst_rot_lr_gc_7): + """Test left right rotation.""" + a = filled_bst_rot_lr_gc_7 + a.insert(7) + assert a.root.value == 8 + assert a.root.parent is None + assert a.root.left.value == 5 + assert a.root.left.left.value == 2 + assert a.root.left.right.value == 7 + assert a.root.right.value == 10 + assert a.root.right.left is None + assert a.root.right.right.value == 12 + + +def test_bst_left_right_root_left_right_add_right(filled_bst_rot_lr_gc_7): + """Test left right rotation.""" + a = filled_bst_rot_lr_gc_7 + a.insert(9) + assert a.root.value == 8 + assert a.root.parent is None + assert a.root.left.value == 5 + assert a.root.left.left.value == 2 + assert a.root.left.right is None + assert a.root.right.value == 10 + assert a.root.right.left.value == 9 + assert a.root.right.right.value == 12 + + +def test_right_left_root_right_left_add_left(filled_bst_rot_rl): + """Test right left rotation.""" + a = filled_bst_rot_rl + a.insert(12) + assert a.root.value == 13 + assert a.root.parent is None + assert a.root.left.value == 10 + assert a.root.left.left.value == 5 + assert a.root.left.right.value == 12 + assert a.root.right.value == 15 + assert a.root.right.left is None + assert a.root.right.right.value == 20 + + +def test_right_left_root_right_left_add_right(filled_bst_rot_rl): + """Test right left rotation.""" + a = filled_bst_rot_rl + a.insert(14) + assert a.root.value == 13 + assert a.root.parent is None + assert a.root.left.value == 10 + assert a.root.left.left.value == 5 + assert a.root.left.right is None + assert a.root.right.value == 15 + assert a.root.right.left.value == 14 + assert a.root.right.right.value == 20 + + +def test_bst_double_rotation_one(): + """Test double rotation one.""" + a = BinarySearchTree([10, 5, 15, 13, 20]) + a.insert(12) + assert a.root.value == 13 + assert a.root.right.value == 15 + assert a.root.left.value == 10 + assert a.root.right.right.value == 20 + assert a.root.left.left.value == 5 + assert a.root.left.right.value == 12 + + +def test_bst_double_rotation_two(): + """Test double rotation one.""" + a = BinarySearchTree([15, 10, 20, 5, 13]) + a.insert(14) + assert a.root.value == 13 + assert a.root.right.value == 15 + assert a.root.left.value == 10 + assert a.root.right.right.value == 20 + assert a.root.right.left.value == 14 + assert a.root.left.left.value == 5 diff --git a/src/test_bst.py b/src/test_bst.py new file mode 100644 index 0000000..6a39301 --- /dev/null +++ b/src/test_bst.py @@ -0,0 +1,504 @@ +"""Test Module for Binary Search Tree.""" +from bst import BinarySearchTree +import pytest + +BST_SIMPLE = [8, 10, 3, 14, 13, 1, 6, 7, 4] +BST_STRAIGHT_LINE = [1, 2, 3, 4, 5, 6, 7] +BST_BIG = [ + 30, 50, 45, 60, 15, 25, 10, 47, 42, + 40, 27, 55, 44, 46, 49, 7, 12, 65, 70, 57, + 63, 52, 23, 4, 9, 11, 13, 21, 24, 26, 28 +] + + +@pytest.fixture +def big_bst(): + """Fixture to fill big bst.""" + from bst import BinarySearchTree + new_tree = BinarySearchTree(BST_BIG) + return new_tree + + +@pytest.fixture +def straight_bst(): + """Fixture to fill bst tree with a straight line of nodes down right side.""" + from bst import BinarySearchTree + new_tree = BinarySearchTree(BST_STRAIGHT_LINE) + return new_tree + + +@pytest.fixture +def filled_bst(): + """Fixture to fill the bst tree with nodes.""" + from bst import BinarySearchTree + new_tree = BinarySearchTree(BST_SIMPLE) + return new_tree + + +def test_insert_5_is_root(): + """Test the insert function.""" + a = BinarySearchTree() + a.insert(5) + assert a.root + + +def test_insert_5_where_root_equals_5(): + """Test the insert funciton.""" + a = BinarySearchTree() + a.insert(5) + assert a.root.value == 5 + + +def test_insert_5_and_10_and_confirm_right(): + """Test the insert function.""" + a = BinarySearchTree() + a.insert(5) + a.insert(10) + assert a.root.right.value == 10 + + +def test_insert_many_numbers(): + """Test the insert function.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.root.right.right.left.value == 13 + assert a.root.left.value == 3 + assert a.root.right.right.value == 14 + assert a.root.value == 8 + assert a.root.left.right.left.value == 4 + + +def test_size_returns_size_of_binary_search_tree(): + """Test that the size method returns size of the bst.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.size() == 9 + + +def test_binary_search_tree_contains_value(): + """Test that the contains method returns True if value in binary search tree.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.contains(4) + + +def test_binary_search_tree_does_not_contain_value(): + """Test that the contains method returns True if value in binary search tree.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.contains(100) is False + + +def test_search_5(): + """Test the search function.""" + a = BinarySearchTree() + a.insert(5) + assert a.search(5) == a.root + + +def test_search_10(): + """Test the search function.""" + a = BinarySearchTree() + a.insert(5) + a.insert(10) + assert a.search(10) == a.root.right + + +def test_search_empty(): + """Test the search function.""" + a = BinarySearchTree() + assert a.search(5) is None + + +def test_search_none(): + """Test the search function.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.search(100) is None + + +def test_depth_zero(): + """Test the depth function.""" + a = BinarySearchTree() + assert a.depth() == 0 + + +def test_depth_one(): + """Test the depth function.""" + a = BinarySearchTree() + a.insert(8) + assert a.depth() == 1 + + +def test_depth_many(): + """Test the depth function.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.depth() == 4 + + +def test_balance(): + """Test the balance function.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + assert a.balance() == 0 + + +def test_balance_unbalanced_right(filled_bst): + """Test the balance function.""" + filled_bst.insert(11) + filled_bst.insert(12) + assert filled_bst.balance() == 2 + + +def test_balance_unbalanced_left(filled_bst): + """Test the balance function.""" + filled_bst.insert(5) + filled_bst.insert(4.5) + assert filled_bst.balance() == -2 + + +def test_in_order_traversal_first_node_traversed_is_1(filled_bst): + """In-order traversal will start with 1.""" + in_order_list = [] + for x in filled_bst._in_order_trav(): + in_order_list.append(x) + assert in_order_list[0] == 1 + + +def test_in_order_traversal_first_node_traversed_is_1_reg(filled_bst): + """In-order traversal's first value from generator will get a 1.""" + assert filled_bst.in_order() == 1 + assert filled_bst.in_order() == 3 + assert filled_bst.in_order() == 4 + assert filled_bst.in_order() == 6 + assert filled_bst.in_order() == 7 + assert filled_bst.in_order() == 8 + assert filled_bst.in_order() == 10 + assert filled_bst.in_order() == 13 + assert filled_bst.in_order() == 14 + + +def test_pre_order_traversal_first_node_traversed_is_8(filled_bst): + """Pre-order traversal will get 8.""" + assert filled_bst.pre_order() == 8 + assert filled_bst.pre_order() == 3 + assert filled_bst.pre_order() == 1 + assert filled_bst.pre_order() == 6 + assert filled_bst.pre_order() == 4 + assert filled_bst.pre_order() == 7 + assert filled_bst.pre_order() == 10 + assert filled_bst.pre_order() == 14 + assert filled_bst.pre_order() == 13 + + +def test_post_order_traversal(filled_bst): + """Post-order traversal.""" + assert filled_bst.post_order() == 1 + assert filled_bst.post_order() == 4 + assert filled_bst.post_order() == 7 + assert filled_bst.post_order() == 6 + assert filled_bst.post_order() == 3 + assert filled_bst.post_order() == 13 + assert filled_bst.post_order() == 14 + assert filled_bst.post_order() == 10 + assert filled_bst.post_order() == 8 + + +def test_delete_empty(): + """Test delete function with empty binary search tree.""" + a = BinarySearchTree() + assert a.delete(5) is None + + +def test_delete_filled_root(filled_bst): + """Test delete of root.""" + a = filled_bst + assert a.size() == 9 + a.delete(8) + assert a.size() == 8 + assert a.in_order() == 1 + assert a.in_order() == 3 + assert a.in_order() == 4 + assert a.in_order() == 6 + assert a.in_order() == 7 + assert a.in_order() == 10 + assert a.in_order() == 13 + assert a.in_order() == 14 + + +def test_delete_end(filled_bst): + """Test delete of root.""" + a = filled_bst + assert a.size() == 9 + a.delete(1) + assert a.size() == 8 + assert a.in_order() == 3 + assert a.in_order() == 4 + assert a.in_order() == 6 + assert a.in_order() == 7 + assert a.in_order() == 8 + assert a.in_order() == 10 + assert a.in_order() == 13 + assert a.in_order() == 14 + + +def test_delete_vertex_of_left_sub_head_in_bst(filled_bst): + """Test delete of a BST's left sub tree's head.""" + a = filled_bst + a.delete(3) + assert a.root.value == 8 + assert a.root.left.value == 4 + assert a.root.left.right.value == 6 + assert a.root.left.right.right.value == 7 + + +def test_delete_vertex_of_lower_vertex_with_2_children(filled_bst): + """Test lower vertex removal at bottom of tree with 2 children.""" + a = filled_bst + a.delete(6) + assert a.root.value == 8 + assert a.root.left.right.value == 7 + assert a.root.left.right.left.value == 4 + assert a.root.left.value == 3 + assert a.root.left.left.value == 1 + assert a.root.right.value == 10 + + +def test_delete(straight_bst): + """Test removal of node from straight line bst.""" + a = straight_bst + a.delete(4) + assert a.root.value == 1 + assert a.root.right.value == 2 + assert a.root.right.right.value == 3 + assert a.root.right.right.right.value == 5 + assert a.root.right.right.right.right.value == 6 + + +def test_node_deletion_from_big_tree_with_grand_children(big_bst): + """Test deletion of node in big bst tree with children and grand children.""" + a = big_bst + a.delete(45) + assert a.root.right.left.value == 46 + assert a.root.right.left.right.value == 47 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.right.right.value == 49 + assert a.root.right.left.right.left is None + + +def test_deletion_from_big_tree_with_great_grand_children(big_bst): + """Test deletion of node in big bst tree with children and grand children.""" + a = big_bst + a.delete(50) + assert a.root.right.value == 52 + assert a.root.right.left.value == 45 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left is None + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_node_deletion_from_big_tree_root(big_bst): + """Test root deletion on big_bst.""" + a = big_bst + a.delete(30) + assert a.root.value == 40 + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left is None + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_node_deletion_from_big_tree_furthest_left(big_bst): + """Test furthest left node deletion on big_bst.""" + a = big_bst + a.delete(4) + assert a.root.value == 30 + assert a.root.left.value == 15 + assert a.root.left.left.value == 10 + assert a.root.left.left.left.value == 7 + assert a.root.left.left.left.left is None + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left.value == 40 + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_big_bst(big_bst): + """Test nodes in proper places in big_bst.""" + a = big_bst + assert a.root.value == 30 + assert a.root.left.value == 15 + assert a.root.left.right.value == 25 + assert a.root.left.right.left.value == 23 + assert a.root.left.left.value == 10 + assert a.root.left.left.right.value == 12 + assert a.root.left.left.right.right.value == 13 + assert a.root.left.left.right.left.value == 11 + assert a.root.left.left.left.value == 7 + assert a.root.left.left.left.right.value == 9 + assert a.root.left.left.left.left.value == 4 + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left.value == 40 + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_big_bst_insert_delete_min_node_with_right_child(big_bst): + """Test deletion of node with min node with right child.""" + a = big_bst + a.insert(22) + a.delete(15) + assert a.root.value == 30 + assert a.root.left.value == 21 + assert a.root.left.right.value == 25 + assert a.root.left.right.left.value == 23 + assert a.root.left.right.left.left.value == 22 + assert a.root.left.left.value == 10 + assert a.root.left.left.right.value == 12 + assert a.root.left.left.right.right.value == 13 + assert a.root.left.left.right.left.value == 11 + assert a.root.left.left.left.value == 7 + assert a.root.left.left.left.right.value == 9 + assert a.root.left.left.left.left.value == 4 + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left.value == 40 + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_big_bst_insert_delete_root_min_node_with_right_child(big_bst): + """Test deletion of root node with min node with right child.""" + a = big_bst + a.insert(41) + a.delete(30) + a = big_bst + assert a.root.value == 40 + assert a.root.left.value == 15 + assert a.root.left.right.value == 25 + assert a.root.left.right.left.value == 23 + assert a.root.left.left.value == 10 + assert a.root.left.left.right.value == 12 + assert a.root.left.left.right.right.value == 13 + assert a.root.left.left.right.left.value == 11 + assert a.root.left.left.left.value == 7 + assert a.root.left.left.left.right.value == 9 + assert a.root.left.left.left.left.value == 4 + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left.value == 41 + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_multiple_delete_to_empty(filled_bst): + """Test that all nodes can be deleted and size output reflects this.""" + a = filled_bst + a.delete(8) + a.delete(10) + a.delete(3) + a.delete(14) + a.delete(13) + a.delete(1) + a.delete(6) + a.delete(7) + a.delete(4) + assert a.size() == 0 diff --git a/src/test_hash_table.py b/src/test_hash_table.py new file mode 100644 index 0000000..d46e8ec --- /dev/null +++ b/src/test_hash_table.py @@ -0,0 +1,56 @@ +"""Test module for Hash Table.""" + +from hash_table import HashTable +import pytest + + +@pytest.fixture +def colin_ben_filled_hash_table(): + """Fixture for a filled hash table using the words from a dictionary text file hashed with the Colin-Ben hashing algorithm.""" + test_table = HashTable('colin-ben') + for line in open('/usr/share/dict/words'): + test_table.set(line, line) + return test_table + + +@pytest.fixture +def additive_filled_hash_table(): + """Fixture for a filled hash table using the words from a dictionary text file hashed with the additive technique.""" + test_table = HashTable('additive') + for line in open('/usr/share/dict/words'): + test_table.set(line, line) + return test_table + + +@pytest.fixture +def colin_ben_filled_hash_table_tiny(): + """Fixture for a filled hash table using the words from a dictionary text file hashed with the Colin-Ben hashing algorithm.""" + test_table = HashTable('colin-ben') + test_table.set('tornado', 25) + test_table.set('kangaroo', 50) + return test_table + + +def test_table_correct(colin_ben_filled_hash_table): + """Testing that get works correctly.""" + count = 0 + for line in open('/usr/share/dict/words'): + count += 1 + if colin_ben_filled_hash_table.get(line) != line: + assert False + assert True + + +def test_table_correct2(additive_filled_hash_table): + """Testing that get works correctly.""" + # import pdb; pdb.set_trace() + for line in open('/usr/share/dict/words'): + if additive_filled_hash_table.get(line) != line: + assert False + assert True + + +def test_table_correct_tiny(colin_ben_filled_hash_table_tiny): + """Testing that get works correctly.""" + assert colin_ben_filled_hash_table_tiny.get('kangaroo') == 50 + assert colin_ben_filled_hash_table_tiny.get('koala') == 'Key not in hash table.' diff --git a/src/test_trie.py b/src/test_trie.py new file mode 100644 index 0000000..bae96b7 --- /dev/null +++ b/src/test_trie.py @@ -0,0 +1,51 @@ +"""Test module for Trie Tree.""" + +from trie import Trie +import pytest + + +SIMPLE_INPUT = ['abort', 'tony', 'borg', 'russia'] +MODERATE_INPUT = ['fast', 'faster', 'fastest', 'fastener', 'breakfasttime'] +COMPLEX_INPUT = ['aaaaaa', 'aardvark', 'aaraal', 'aapppp', 'aapear', 'tornado', 'ado', 'tor', 'to', 'o', 'oo', 'oooo', 'elephant', 'elevate', 'elephants'] + +PARAMS_CONTAINS_SIMPLE = [ + ('abort', True), + ('tony', True), + ('ab', False), + ('z', False), + ('a', False), + ('t', False) +] + + +@pytest.fixture +def simple_trie(): + """Trie with simple input.""" + a = Trie() + for each in SIMPLE_INPUT: + a.insert(each) + return a + + +@pytest.fixture +def moderate_trie(): + """Trie with moderate input.""" + a = Trie() + for each in MODERATE_INPUT: + a.insert(each) + return a + + +@pytest.fixture +def complex_trie(): + """Trie with complex input.""" + a = Trie() + for each in COMPLEX_INPUT: + a.insert(each) + return a + + +@pytest.mark.parametrize('n, result', PARAMS_CONTAINS_SIMPLE) +def test_contains_simple(simple_trie, n, result): + """Test contains function.""" + assert simple_trie.contains(n) == result diff --git a/src/test_trie_trav.py b/src/test_trie_trav.py new file mode 100644 index 0000000..d23f686 --- /dev/null +++ b/src/test_trie_trav.py @@ -0,0 +1,76 @@ +from trie_trav import Trie +import pytest + +SIMPLE_INPUT = ['abort', 'tony', 'borg', 'russia'] +MODERATE_INPUT = ['fast', 'faster', 'fastest', 'fastener', 'breakfasttime'] +COMPLEX_INPUT = ['aaaaaa', 'aardvark', 'aaraal', 'aapppp', 'aapear', 'tornado', 'ado', 'tor', 'to', 'o', 'oo', 'oooo', 'elephant', 'elevate', 'elephants'] + +PARAMS_SIMPLE = [ + ('rus', ['s', 'i', 'a']), + ('o', ['r', 't', 'n', 'y', 'r', 'g']), + ('a', ['b', 'o', 'r', 't']), + ('r', ['t', 'g']) +] + +PARAMS_MODERATE = [ + ('fast', ['e', 'r', 's', 't', 'n', 'e', 'r', 't', 'i', 'm', 'e']), + ('f', ['a', 's', 't', 'e', 'r', 's', 't', 'n', 'e', 'r', 'a', 's', 't', 't', 'i', 'm', 'e']), + ('a', ['s', 't', 'e', 'r', 's', 't', 'n', 'e', 'r', 'k', 'f', 'a', 's', 't', 'i', 'm', 'e']), + ('faste', ['r', 's', 't', 'n', 'e', 'r']), + ('t', ['r', 'r', 's', 't', 'n', 'e', 'r', 't', 'i', 'm', 'e']), + ('faster', []), +] + +PARAMS_COMPLEX = [ + ('a', ['d', 'o', 'a', 'a', 'a', 'a', 'a', 'r', 'd', 'v', 'a', 'r', 'k', 'a', 'a', 'l', 'p', 'p', 'p', 'p', 'e', 'a', 'r', 'd', 'o', 'n', 't', 's', 't', 'e']), + ('', []), + ('', []), +] + + +@pytest.fixture +def simple_trie(): + """Trie with simple input.""" + a = Trie() + for each in SIMPLE_INPUT: + a.insert(each) + return a + + +@pytest.fixture +def moderate_trie(): + """Trie with moderate input.""" + a = Trie() + for each in MODERATE_INPUT: + a.insert(each) + return a + + +@pytest.fixture +def complex_trie(): + """Trie with complex input.""" + a = Trie() + for each in COMPLEX_INPUT: + a.insert(each) + return a + + +@pytest.mark.parametrize('n, result', PARAMS_SIMPLE) +def test_traversal_simple(simple_trie, n, result): + """Test traversal with input 'o'.""" + g = simple_trie.traversal(n) + assert list(g) == result + + +@pytest.mark.parametrize('n, result', PARAMS_MODERATE) +def test_traversal_moderate(simple_trie, n, result): + """Test traversal with input 'o'.""" + g = simple_trie.traversal(n) + assert list(g) == result + + +@pytest.mark.parametrize('n, result', PARAMS_COMPLEX) +def test_traversal_complex(simple_trie, n, result): + """Test traversal with input 'o'.""" + g = simple_trie.traversal(n) + assert list(g) == result diff --git a/src/trie.py b/src/trie.py new file mode 100644 index 0000000..be7fd03 --- /dev/null +++ b/src/trie.py @@ -0,0 +1,93 @@ +"""Module for Trie tree.""" + +# TRIE TREE (TT) +# +# CodeFellows 401d5 +# Submission Date: +# +# Authors: Colin Lamont +# Ben Shields +# +# URL: + + +class Node(object): + """Node Class contains a value and a dictionary of nodes.""" + + def __init__(self, val=None): + """Initialize the Node class with val and empty nodes dictionary.""" + self.val = val + self.nodes = {} + + +class Trie(object): + """Trie class, which is the Trie tree.""" + + """ + insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. + contains(self, string): will return True if the string is in the trie, False if not. + size(self): will return the total number of words contained within the trie. 0 if empty. + remove(self, string): will remove the given string from the trie. If the word does not exist, will raise an appropriate exception + """ + + def __init__(self): + """Initialize the Trie class with root Node with ('*') and size of 0.""" + self.root = Node('*') + self._size = 0 + + def insert(self, word): + """Insert method, which takes a word and inserts each letter of the word into the Trie, with pointer to next Node or $ if end.""" + node = self.root + new_node = None + new_word = False + for each in word: + if each in node.nodes: + node = node.nodes[each] + continue + new_word = True + new_node = Node(each) + node.nodes[each] = new_node + node = new_node + if new_word: + self._size += 1 + node.nodes['$'] = None + + def contains(self, word): + """The contains method returns True if the word is found in the Trie tree, or False if not.""" + node = self.root + for each in word: + if each in node.nodes: + node = node.nodes[each] + else: + return False + if '$' in node.nodes: + return True + return False + + def size(self): + """The size method returns the number of words in the Trie.""" + return self._size + + def remove(self, word): + """The remove method removes the word from the Trie.""" + node_list = [] + node = self.root + for each in word: + if each in node.nodes: + node_list.append(node.nodes[each]) + node = node.nodes[each] + last = node_list.pop() + if '$' not in last.nodes: + return + del last.nodes['$'] + for i in range(len(node_list)): + last_val = last.val + last = node_list.pop() + if '$' in last.nodes: + self._size -= 1 + break + if len(last.nodes) > 1: + del last.nodes[last_val] + self._size -= 1 + break + del last.nodes[last_val] diff --git a/src/trie_trav.py b/src/trie_trav.py new file mode 100644 index 0000000..f51da08 --- /dev/null +++ b/src/trie_trav.py @@ -0,0 +1,157 @@ +"""Module for Trie tree with traversal.""" + +# TRIE TREE TRAVERSAL (TTT) +# +# CodeFellows 401d5 +# Submission Date: +# +# Authors: Colin Lamont +# Ben Shields +# +# URL: + +from collections import OrderedDict + + +class Node(object): + """Node Class contains a value and a dictionary of nodes.""" + + def __init__(self, val=None): + """Initialize the Node class with val and empty nodes dictionary.""" + self.val = val + self.nodes = OrderedDict() + + +class Trie(object): + """Trie class, which is the Trie tree.""" + + """ + insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. + contains(self, string): will return True if the string is in the trie, False if not. + size(self): will return the total number of words contained within the trie. 0 if empty. + remove(self, string): will remove the given string from the trie. If the word does not exist, will raise an appropriate exception. + traversal(self, start=None): a generator yielding values in the tree in depth-first order. + """ + + def __init__(self): + """Initialize the Trie class with root Node with ('*') and size of 0.""" + self.root = Node('*') + self._size = 0 + + def insert(self, word): + """Insert method, which takes a word and inserts each letter of the word into the Trie, with pointer to next Node or $ if end.""" + node = self.root + new_node = None + new_word = False + counter = 0 + for each in word: + if each in node.nodes: + if counter == (len(word) - 1) and '$' not in node.nodes: + new_word = True + new_node = Node(each) + node.nodes[each] = new_node + node = new_node + break + counter += 1 + node = node.nodes[each] + continue + counter += 1 + new_word = True + new_node = Node(each) + node.nodes[each] = new_node + node = new_node + if new_word: + self._size += 1 + node.nodes['$'] = None + + def contains(self, word): + """The contains method returns True if the word is found in the Trie tree, or False if not.""" + node = self.root + for each in word: + if each in node.nodes: + node = node.nodes[each] + else: + return False + if '$' in node.nodes: + return True + return False + + def size(self): + """The size method returns the number of words in the Trie.""" + return self._size + + def remove(self, word): + """The remove method removes the word from the Trie.""" + node_list = [] + node = self.root + for each in word: + if each in node.nodes: + node_list.append(node.nodes[each]) + node = node.nodes[each] + else: + raise(IndexError) + last = node_list.pop() + if '$' not in last.nodes: + return + del last.nodes['$'] + for i in range(len(node_list)): + last_val = last.val + last = node_list.pop() + if '$' in last.nodes: + self._size -= 1 + break + if len(last.nodes) > 1: + del last.nodes[last_val] + self._size -= 1 + break + del last.nodes[last_val] + self._size -= 1 + + def traversal(self, start=None): + """A generator yielding values in the tree in depth first order.""" + if not start: + stack = [] + stack.append(self.root) + while len(stack) > 0: + node = stack.pop() + if node.val != '*': + yield node.val + items = node.nodes.items() + items = reversed(items) + nodes = OrderedDict(items) + for each in nodes: + if len(node.nodes) == 1 and '$' in node.nodes: + break + stack.append(nodes[each]) + else: + compare = '' + index = 0 + comparing = False + found = False + stack = [] + stack.append(self.root) + # import pdb; pdb.set_trace() + while len(stack) > 0: + node = stack.pop() + if node.val != '*' and found: + yield node.val + if node.val == start[index]: + comparing = True + compare += start[index] + index += 1 + if index == len(start): + found = True + comparing = False + compare = '' + index = 0 + elif comparing: + comparing = False + compare = '' + index = 0 + items = node.nodes.items() + items = reversed(items) + nodes = OrderedDict(items) + for each in nodes: + if len(node.nodes) == 1 and '$' in node.nodes: + break + stack.append(nodes[each])