diff --git a/README.md b/README.md index 011859c..66d0f71 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,8 @@ Code Fellows Dev Accelerator: This repository will be for implementations of cla parens.py: -Includes a function balanceness() that takes string and determines if it is 'open', 'balanced', or 'broken', depending on the sequence of parenthesis contained. 'Open', 'balanced', and 'broken' are respectively determined as more leading '('s than ')'s, equal number of leading '(' and ')', and any sequence including ')' that do not have a preceding '('. 'Open' strings are represented by returning a 1, 'balanced' by 0, and 'broken' by -1, with an emptry string being considered 'balanced'. \ No newline at end of file +Includes a function balanceness() that takes string and determines if it is 'open', 'balanced', or 'broken', depending on the sequence of parenthesis contained. 'Open', 'balanced', and 'broken' are respectively determined as more leading '('s than ')'s, equal number of leading '(' and ')', and any sequence including ')' that do not have a preceding '('. 'Open' strings are represented by returning a 1, 'balanced' by 0, and 'broken' by -1, with an emptry string being considered 'balanced'. + +bst.py: +Binary search tree is a tree where each node has a left child and a right +child and every left child is smaller and every right child is larger. We implement it with a dictionary where each key is the value of the node and each value has three things in it, depth, left child and right child. \ No newline at end of file diff --git a/bst.py b/bst.py new file mode 100755 index 0000000..df61139 --- /dev/null +++ b/bst.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +import random +import subprocess + + +class Bst(object): + """Binary Search Tree using a dictionary""" + def __init__(self, value=None): + self.tree = {} + self.top = None + self._size = 0 + self._depth = 0 + + if value is not None: + self.tree[value] = {'depth': 1} + self.top = value + self._size += 1 + self._depth += 1 + + def insert(self, value): + """Insert a node with value in order. + + In value is already present, it is ignored.""" + current = self.top + if current is None: + self.tree[value] = {'depth': 1} + self.top = value + self._size += 1 + self._depth += 1 + return + depth = 1 # starts trying to insert at depth 2 + while current is not None: + depth += 1 + if current == value: + return + if current < value: + traverse = self.tree[current].get('right') + child = 'right' + else: + traverse = self.tree[current].get('left') + child = 'left' + if traverse is None: + #actual insert + self.tree[value] = {'depth': depth} + self.tree[current][child] = value + self._size += 1 + if depth > self._depth: + self._depth = depth + return + current = traverse + + + def balance(self): + """Returns the balance of the tree: + + Returns a positive representing how much deeper the tree is on the + right site or negative if the left is longer. + Return 0 if the tree is balanced (same depth on both sides) + + """ + left_deep = 0 + right_deep = 0 + for node, v in self.tree.items(): + if self.top > node: + if left_deep < v['depth']: + left_deep = v['depth'] + elif self.top < node: + if right_deep < v['depth']: + right_deep = v['depth'] + return right_deep - left_deep + + def contains(self, value): + """Returns true if value is in the tree.""" + return value in self.tree + + def size(self): + """Returns the number of nodes in the tree.""" + return self._size + + def depth(self): + """Returns the depth of the tree.""" + return self._depth + + def get_dot(self): + """return the tree with root 'self' as a dot graph for visualization""" + return "digraph G{\n%s}" % ("" if not self.tree else ( + "\n%s\n" % ( + # "\t%s;\n%s\n" % ( + # list(self.tree), + "\n".join(self._get_dot(self.top)) + ) + )) + + + def _get_dot(self, current): + """recursively prepare a dot graph entry for this node.""" + left = self.tree[current].get('left') + right = self.tree[current].get('right') + if left is not None: + yield "\t%s -> %s;" % (current, left) + for i in self._get_dot(left): + yield i + elif right is not None: + r = random.randint(0, 1e9) + yield "\tnull%s [shape=point];" % r + yield "\t%s -> null%s;" % (current, r) + if right is not None: + yield "\t%s -> %s;" % (current, right) + for i in self._get_dot(right): + yield i + elif left is not None: + r = random.randint(0, 1e9) + yield "\tnull%s [shape=point];" % r + yield "\t%s -> null%s;" % (current, r) + + +def main(): + """Best case and worst case are the same.""" + tree = Bst() + for num in reversed(range(10)): + tree.insert(num) + for num in range(10, 15): + tree.insert(num) + dot_graph = tree.get_dot() + t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) + t.communicate(dot_graph) + + +if __name__ == '__main__': + main() diff --git a/test_bst.py b/test_bst.py new file mode 100644 index 0000000..2cadfec --- /dev/null +++ b/test_bst.py @@ -0,0 +1,91 @@ +import bst +import pytest + + +def test_Bst(): + """Constructing a bst tree""" + tree = bst.Bst() + assert tree.tree == {} + tree = bst.Bst(5) + assert tree.tree == {5: {'depth': 1}} + + +def test_insert(empty_tree): + """Test insert into a tree""" + tree = empty_tree + tree.insert(20) + assert tree.tree == {20: {'depth': 1}} + tree.insert(30) + expect = {20: {'depth': 1, + 'right': 30}, + 30: {'depth': 2}} + assert tree.tree == expect + tree.insert(40) + expect = {20: {'depth': 1, + 'right': 30}, + 30: {'depth': 2, + 'right': 40}, + 40: {'depth': 3}} + assert tree.tree == expect + tree.insert(10) + expect = {20: {'depth': 1, + 'left': 10, + 'right': 30}, + 30: {'depth': 2, + 'right': 40}, + 40: {'depth': 3}, + 10: {'depth': 2}} + assert tree.tree == expect + + +def test_size_emptree(empty_tree): + t = empty_tree + assert t.size() == 0 + + +def test_size_tree(filled_tree): + t = filled_tree + assert t.size() == 14 + + +def test_depth_emptree(empty_tree): + t = empty_tree + assert t.depth() == 0 + + +def test_depth_tree(filled_tree): + t = filled_tree + assert t.depth() == 10 + +def test_balance(empty_tree, filled_tree): + t = empty_tree + assert t.balance() == 0 + t.insert(3) + assert t.balance() == 0 + t = filled_tree + assert t.balance() == -5 + +def test_contains(filled_tree): + t = filled_tree + for num in reversed(range(10)): + assert t.contains(num) is True + for num in range(10, 14): + assert t.contains(num) is True + for num in range(20, 25): + assert t.contains(num) is False + + +@pytest.fixture(scope='function') +def filled_tree(): + tree = bst.Bst() + for num in reversed(range(10)): + tree.insert(num) + for num in range(10, 14): + tree.insert(num) + return tree + + +@pytest.fixture(scope='function') +def empty_tree(): + tree = bst.Bst() + return tree