diff --git a/README.md b/README.md new file mode 100644 index 0000000..e3a7e22 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +README + +A set of data structures that allow for the manipulation and implementation of Binary Search Trees. + + +BST.py + + BinarySearchTree is a class, that accepts a list or tuple of integers or floats on initiation and creates an instance of a Binary search tree. + + ********* + *Methods* + ********* + + *search- Accepts one parameter as integer or float. Will search instance of BST for value and return said Node if found. Returns None if not found. + + *insert- Accepts one parameter as integer or float. Will create a new Node with given value and insert into the BST properly. + + *breadth_first- Returns a breadth first traversal generator of the current BST. + + *Depth First Traversal* + + *pre_order- Returns a depth first traversal, pre order, generator of the current BST. + + *in_order- Returns a depth first traversal, in order, generator of the current BST. + + *post_order- Returns a depth first traversal, post order, generator of the current BST. diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..15f9afe --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +from setuptools import setup + +extra_packages = { + 'testing': ['ipython', 'pytest', 'pytest-cov', 'tox'] +} + +setup( + name='data-structures', + desctription='A variety of data structures built with Python.', + version='0.1', + author='James Salamonsen' 'Alex Short, + author_email='jamessalamonsen@gmail.com', + license='MIT', + py_modules=[], + package_dir={'': 'src'}, + install_requires=[], + extras_require=extra_packages, + entry_points={ + 'console_scripts': [ + ] + } +) diff --git a/src/BST.py b/src/BST.py index 4d91c0a..502cace 100644 --- a/src/BST.py +++ b/src/BST.py @@ -16,14 +16,14 @@ def __init__(self, iterable=None): # import pdb; pdb.set_trace() """This will sety what wwe will be iterating through.""" self.visited = [] - # self.list = [] + self.list = [] self.size = 0 self.root = None self.iterable = iterable if iterable is not None: if type(iterable) in [list, tuple]: for element in iterable: - # self.list.append(element) + self.list.append(element) self.insert(element) def insert(self, entry): @@ -43,7 +43,7 @@ def insert(self, entry): else: curr.right = Node(entry) self.size += 1 - Node.parent = curr + curr.right.parent = curr elif entry < curr.val: if curr.left: curr = curr.left @@ -51,7 +51,7 @@ def insert(self, entry): else: curr.left = Node(entry) self.size += 1 - Node.parent = curr + curr.left.parent = curr else: return @@ -61,7 +61,6 @@ def search(self, entry): raise TypeError("NUMBERS!!!!! numbers...") else: curr = self.root - # import pdb; pdb.set_trace() while curr: if entry > curr.val: if curr.right: @@ -74,6 +73,15 @@ def search(self, entry): else: return curr + def depth_first(self, entry): + if entry is None: + return 0 + left_depth = self.depth_first(entry.left) + right_depth = self.depth_first(entry.right) + if (left_depth > right_depth): + return left_depth + 1 + return right_depth + 1 + def breadth_first(self): self.nodes_to_visit = [] curr = self.root @@ -137,13 +145,77 @@ def post_order(self): for node_data in self.post_order_trav(): yield node_data - def deletion(): + def check_that_balance(self, target=None): + """check the balance of your treeeee""" + if self.size == 0: + return 0 + if not target: + target = self.root + else: + target = self.search(target) + return self.depth_first(target.right) - self.depth_first(target.left) + + def find_min_depth(self, target): + curr = target + while curr.left is not None: + curr = curr.left + return curr + + def find_max_depth(self, target): + curr = target + while curr.right is not None: + curr = curr.right + return curr + + def deletion(self, target): """nope""" - pass + delete_node = self.search(target) + if delete_node is None: + return 'This is the end!' + else: + self.size -= 1 + if delete_node.left is None and delete_node.right is None: + if delete_node.val > delete_node.parent.val: + delete_node.parent.right = None + else: + delete_node.parent.left = None + elif delete_node.left and not delete_node.right: + if delete_node.val > delete_node.parent.val: + delete_node.parent.right = delete_node.left + delete_node.left.parent = delete_node.parent + else: + delete_node.parent.left = delete_node.left + delete_node.left.parent = delete_node.parent + elif delete_node.right and not delete_node.left: + if delete_node.val > delete_node.parent.val: + delete_node.parent.right = delete_node.right + delete_node.right.parent = delete_node.parent + else: + delete_node.parent.left = delete_node.right + delete_node.left.parent = delete_node.parent + else: + if self.check_that_balance(target) > 0: + min_val = self.find_min_depth(delete_node.right) + delete_node.val = min_val.val + if min_val.right is None: + min_val.parent.left = None + min_val.parent = None + else: + min_val.val = min_val.right.val + min_val.right.parent = None + min_val.right = None + else: + max_val = self.find_max_depth(delete_node.left) + delete_node.val = max_val.val + if max_val.left is None: + max_val.parent.right = None + max_val.parent = None + else: + max_val.val = max_val.left.val + max_val.left.parent = None + max_val.left = None -if __name__ == '__main__': - import sys - b = BinarySearchTree([5,3,7,2,8,4,9,1]) +if __name__ == '__main__': # pragma: no cover + b = BinarySearchTree([5, 3, 7, 2, 8, 4, 9, 1]) gen = b.in_order() - for i in range(5): - print(next(gen)) + [i for i in gen] diff --git a/src/binary_tree.py b/src/binary_tree.py deleted file mode 100644 index 9047d78..0000000 --- a/src/binary_tree.py +++ /dev/null @@ -1,64 +0,0 @@ -'''Constructing the Binary Tree Data Structure''' - - -class BiTree(): - - def __init__(self, root): - self.left = None - self.right = None - self.root = root - - def getLeftChi(self): - return self.left - - def getRightChi(self): - return self.right - - def setNodeVal(self, val): - self.root = val - - def getNodeVal(self): - return self.root - - def insRight(self, newNode): - if self.right == None: - self.right = BiTree(newNode) - else: - tree = BiTree(newNode) - tree.right = self.right - self.right = tree - - def insLeft(self, newNode): - if self.left == None: - self.left = BiTree(newNode) - else: - tree = BiTree(newNode) - tree.left = self.left - self.left = tree - - -class Node(object): - def __init__(self, val, left=None, iterable=None): - """Node for tree.""" - self.root = val - self.left = left - self.right = iterable - - -class BinarySearchTree(): - if not self._root: - self._root = Node(val) - self._size += 1 - - else: - curr = self._root - while curr: - if val > curr.val: - if curr.right: - curr = curr.right - else: - curr.right = Node(val) - elif val < curr.val: - curr.left = Node(val) - else: - return \ No newline at end of file diff --git a/src/challenge1.py b/src/challenge1.py deleted file mode 100644 index e3596b5..0000000 --- a/src/challenge1.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Code Challenge 1.""" - - -def check_for_loop(the_list): - """Check for a cyclical linked list.""" - curr = the_list.head - store_nodes = [] - while curr: - if curr.next in store_nodes: - return True - store_nodes.append(curr.next) - curr = curr.next - return False diff --git a/src/dll.py b/src/dll.py deleted file mode 100644 index da4a767..0000000 --- a/src/dll.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Constructing the Double Linked List data structure.""" - - -class DNode(object): - """.""" - - def __init__(self, val, next, prev): - """Create a new Node object with a val and a Node it points to.""" - self.val = val - self.next = next - self.prev = prev - - -class DLinkedList(object): - """Define a linked list and its methods.""" - - def __init__(self): - """Create a new empty Doubly Linked List.""" - self.head = None - self.tail = None - self.length = 0 - - def push(self, val): - """Add a new DNode to the head of the list.""" - new_next = self.head - self.head = DNode(val, self.head, None) - if self.head.next is None: - self.tail = self.head - else: - new_next.prev = self.head - self.length += 1 - - def append(self, val): - """Add a new DNode to the tail of the list.""" - new_prev = self.tail - self.tail = DNode(val, None, self.tail) - if self.tail.prev is None: - self.head = self.tail - else: - new_prev.next = self.tail - self.length += 1 - - def pop(self): - """Remove the head of the list and returns its value.""" - if self.head is not None: - ret_val = self.head.val - if self.head.next is not None: - self.head.next.prev = None - self.head = self.head.next - if self.head is None: - self.tail = None - self.length -= 1 - return ret_val - else: - raise IndexError('Linked list is empty') - - def shift(self): - """Remove the tail of the list and returns its value.""" - if self.tail is not None: - ret_val = self.tail.val - if self.tail.prev is not None: - self.tail.prev.next = None - self.tail = self.tail.prev - if self.tail is None: - self.head = None - self.length -= 1 - return ret_val - else: - raise IndexError('Linked list is empty') - - def remove(self, val): - """Remove a given node from the list.""" - current_node = self.head - val_exists = False - if self.head.val == val: - self.head = self.head.next - self.head.prev = None - self.length -= 1 - val_exists = True - if self.tail.val == val: - self.tail = self.tail.prev - self.tail.next = None - self.length -= 1 - val_exists = True - while current_node.next and not val_exists: - if current_node.next.val == val: - current_node.next.next.prev = current_node - current_node.next = current_node.next.next - self.length -= 1 - val_exists = True - break - current_node = current_node.next - if not val_exists: - raise IndexError('Node not in list') - - def __len__(self): - """Return the length of the double list.""" - return self.length diff --git a/src/linked_list.py b/src/linked_list.py deleted file mode 100644 index 666551b..0000000 --- a/src/linked_list.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Constructing the Linked List data structure.""" - - -class Node(object): - """.""" - - def __init__(self, val, next): - """Create a new Node object with a val and a Node it points to.""" - self.val = val - self.next = next - - -class LinkedList(object): - """Define a linked list and its methods.""" - - def __init__(self, iterable=None): - """Create a new empty Linked List.""" - if type(iterable) in [list, tuple, str]: - self.head = None - self.length = 0 - for val in iterable: - self.push(val) - else: - self.head = None - self.length = 0 - - def push(self, val): - """Add a new Node to the head of the list.""" - self.head = Node(val, self.head) - self.length += 1 - - def pop(self): - """Remove the head of the list and returns its value.""" - if self.head is not None: - ret_val = self.head.val - self.head = self.head.next - self.length -= 1 - return ret_val - else: - raise IndexError('Linked list is empty') - - def size(self): - """Return the length of the list.""" - return self.length - - def display(self): - """Return a string with all the list Node values and their order.""" - current_node = self.head - the_str = '(' - while current_node: - the_str += str(current_node.val) - if current_node.next is None: - the_str += ')' - else: - the_str += ', ' - current_node = current_node.next - return the_str - - def __len__(self): - """Return the length of the list.""" - return self.length - - def __repr__(self): - """Return what the display method returns.""" - return self.display() - - def remove(self, node): - """Remove a given node from the list.""" - current_node = self.head - node_exists = False - if self.head is node: - self.head = self.head.next - self.length -= 1 - node_exists = True - while current_node.next and not node_exists: - if current_node.next is node: - current_node.next = current_node.next.next - self.length -= 1 - node_exists = True - break - current_node = current_node.next - if not node_exists: - raise IndexError('Node not in list') - - def search(self, val): - """Return the first Node containing the value.""" - curr = self.head - - while curr: - if curr.val == val: - return curr - curr = curr.next - return None diff --git a/src/stack.py b/src/stack.py deleted file mode 100644 index 4a748fb..0000000 --- a/src/stack.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Constructing the Stack data structure.""" - - -from linked_list import LinkedList - - -class Stack(object): - """Define a stack and its methods.""" - - def __init__(self, iterable=None): - """Create a new empty stack.""" - self.newlinkedlist = LinkedList(iterable) - - def push(self, val): - """Add a new Node to the head of the stack.""" - return self.newlinkedlist.push(val) - - def pop(self): - """Remove the head of the stack and returns its value.""" - return self.newlinkedlist.pop() - - def __len__(self): - """Return the length of the stack.""" - return self.newlinkedlist.size() diff --git a/src/test_BST.py b/src/test_BST.py deleted file mode 100644 index 65b94b7..0000000 --- a/src/test_BST.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Test the tree.py module.""" -import pytest -from BST import BinarySearchTree - - -b = BinarySearchTree([5, 3, 7, 2, 8, 4, 9, 1]) - - -@pytest.fixture -def bitr(): - """Init a new Binary Tree.""" - tree = BinarySearchTree() - return tree - - -@pytest.fixture -def bitr_iter(): - """Init a new Binary tree from an iterable.""" - gen = b.in_order() - return gen - - -def test_tree_empty(): - """Instantiate a new Binary tree, that should be an empty list.""" - b = BinarySearchTree([]) - assert b.size == 0 - - -def test_bitr_iter_in_order(): - """Create a Bin tree with an iterable and check it is equal.""" - gen = b.in_order() - b_list = [item for item in gen] - assert b_list == [1, 2, 3, 4, 5, 7, 8, 9] - - -def test_bitr_iter_pre_order(): - """Create a Bin tree with an iterable and check it is equal.""" - gen = b.pre_order() - b_list = [item for item in gen] - assert b_list == [5, 3, 2, 1, 4, 7, 8, 9] - - -def test_bitr_iter_post_order(): - """Create a Bin tree with an iterable and check it is equal.""" - gen = b.post_order() - b_list = [item for item in gen] - assert b_list == [1, 2, 4, 3, 9, 8, 7, 5] - - -def test_bitr_insert(): - """Push with large value and no bubbling.""" - b.insert(30) - gen = b.in_order() - b_list = [item for item in gen] - assert b_list == [1, 2, 3, 4, 5, 7, 8, 9, 30] - b.insert(2.5) - gen = b.in_order() - b_list = [item for item in gen] - assert b_list == [1, 2, 2.5, 3, 4, 5, 7, 8, 9, 30] - - -def test_bitr_search(): - assert b.search(2).left.val == 1 - - -# def test_bitr_breadth(): -# bist = [] -# bf = b.breadth_first() -# for i in bf: -# bist.append(i) - -# def test_ - - - diff --git a/test_BST.py b/test_BST.py new file mode 100644 index 0000000..7ef9663 --- /dev/null +++ b/test_BST.py @@ -0,0 +1,180 @@ +"""Test the tree.py module.""" +import pytest +from BST import BinarySearchTree + +a = BinarySearchTree([12, 42, 4, 87, 2, 65, 0]) + +b = BinarySearchTree([5, 3, 7, 2, 8, 4, 9, 1]) + +c = BinarySearchTree([-2, -6, 22.55, 0, -94, 2, -1]) + + +@pytest.fixture +def bitr(): # pragma: no cover + """Init a new Binary Tree.""" + tree = BinarySearchTree() + return tree + + +@pytest.fixture +def bitr_iter(): # pragma: no cover + """Init a new Binary tree from an iterable.""" + return a.iterable + return b.iterable + return c.iterable + + +def test_tree_empty(): + """Instantiate a new Binary tree, that should be an empty list.""" + b = BinarySearchTree([]) + assert b.size == 0 + + +def test_type_error_insert(): + """Test for type error in insert function""" + with pytest.raises(TypeError): + b.insert('not a number') + + +def test_type_error_search(): + """Test for type error in the search function""" + with pytest.raises(TypeError): + b.search('still not a number') + + +def test_bitr_search(): + """Test the seartch function""" + assert a.search(12).left.val == 4 + assert a.search(4).left.val == 2 + assert a.search(87).left.val == 65 + assert b.search(3).left.val == 2 + assert b.search(5).left.val == 3 + assert c.search(-2).left.val == -6 + assert c.search(0).left.val == -1 + assert c.search(-6).left.val == -94 + + +def test_bitr_iter_in_order(): + """Test the in order funciton.""" + gena = a.in_order() + a_list = [item for item in gena] + assert a_list == [0, 2, 4, 12, 42, 65, 87] + assert a_list[0] == 0 + assert a_list[1] == 2 + assert a_list[2] == 4 + assert len(a_list) == 7 + genb = b.in_order() + b_list = [item for item in genb] + assert b_list == [1, 2, 3, 4, 5, 7, 8, 9] + assert b_list[0] == 1 + assert b_list[1] == 2 + assert b_list[2] == 3 + assert len(b_list) == 8 + genc = c.in_order() + c_list = [item for item in genc] + assert c_list == [-94, -6, -2, -1, 0, 2, 22.55] + assert c_list[0] == -94 + assert c_list[1] == -6 + assert c_list[2] == -2 + assert len(c_list) == 7 + + +def test_bitr_iter_pre_order(): + """Test the pre order funciton.""" + gena = a.pre_order() + a_list = [item for item in gena] + assert a_list == [12, 4, 2, 0, 42, 87, 65] + assert a_list[0] == 12 + assert a_list[1] == 4 + assert a_list[6] == 65 + assert len(a_list) == 7 + genb = b.pre_order() + b_list = [item for item in genb] + assert b_list == [5, 3, 2, 1, 4, 7, 8, 9] + assert b_list[0] == 5 + assert b_list[1] == 3 + assert b_list[6] == 8 + assert len(b_list) == 8 + genc = c.pre_order() + c_list = [item for item in genc] + assert c_list == [-2, -6, -94, 22.55, 0, -1, 2] + assert c_list[0] == -2 + assert c_list[1] == -6 + assert c_list[6] == 2 + assert len(c_list) == 7 + + +def test_bitr_iter_post_order(): + """Test the post order funciton.""" + gena = a.post_order() + a_list = [item for item in gena] + assert a_list == [0, 2, 4, 65, 87, 42, 12] + assert a_list[0] == 0 + assert a_list[3] == 65 + assert a_list[2] == 4 + assert len(a_list) == 7 + gen = b.post_order() + b_list = [item for item in gen] + assert b_list == [1, 2, 4, 3, 9, 8, 7, 5] + assert b_list[0] == 1 + assert b_list[3] == 3 + assert b_list[2] == 4 + assert len(b_list) == 8 + genc = c.post_order() + c_list = [item for item in genc] + assert c_list == [-94, -6, -1, 2, 0, 22.55, -2] + assert c_list[0] == -94 + assert c_list[3] == 2 + assert c_list[2] == -1 + assert len(c_list) == 7 + + +def test_bitr_breadth(): + """Test the breadth first traversal.""" + abf = a.breadth_first() + assert [ab.val for ab in abf] == [12, 4, 42, 2, 87, 0, 65] + bbf = b.breadth_first() + assert [bb.val for bb in bbf] == [5, 3, 7, 2, 4, 8, 1, 9] + cbf = c.breadth_first() + assert [cb.val for cb in cbf] == [-2, -6, 22.55, -94, 0, -1, 2] + + +def test_bitr_insert(): + """Push the insert function.""" + a.insert(30) + gena = a.in_order() + a_list = [item for item in gena] + assert a_list == [0, 2, 4, 12, 30, 42, 65, 87] + a.insert(2.5) + gena = a.in_order() + a_list = [item for item in gena] + assert a_list == [0, 2, 2.5, 4, 12, 30, 42, 65, 87] + b.insert(1.1) + genb = b.in_order() + b_list = [item for item in genb] + assert b_list == [1, 1.1, 2, 3, 4, 5, 7, 8, 9] + b.insert(-90) + genb = b.in_order() + b_list = [item for item in genb] + assert b_list == [-90, 1, 1.1, 2, 3, 4, 5, 7, 8, 9] + c.insert(31) + genc = c.in_order() + c_list = [item for item in genc] + assert c_list == [-94, -6, -2, -1, 0, 2, 22.55, 31] + c.insert(22.54) + genc = c.in_order() + c_list = [item for item in genc] + assert c_list == [-94, -6, -2, -1, 0, 2, 22.54, 22.55, 31] + + +def test_find_min_depth(): + """Test if the min depth function returns the farthest left.""" + assert a.find_min_depth(a.root).val == 0 + + def test_delete_empty(): + """.""" + pass + + def test_max_depth(): + """Test if the max depth function returns the farthest right.""" + pass diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..36db6d0 --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py27, py36 + +[testenv] +commands = py.test src --cov=src --cov-report term-missing +deps = + pytest + pytest-cov \ No newline at end of file