Skip to content
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
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.
130 changes: 130 additions & 0 deletions bst.py
Original file line number Diff line number Diff line change
@@ -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 = {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A dict of dicts. This is going to be interesting!

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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the upcoming need to traverse, you might consider breaking the code responsible for determining where to traverse to into a support method.



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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better than what we saw in class today. Still O(n) though.


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."""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true for you. Sad though. You should try implementing the binary search algorithm at some point.

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()
91 changes: 91 additions & 0 deletions test_bst.py
Original file line number Diff line number Diff line change
@@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is too long. make some support methods that do the work, then call them. Or break this test into a series of tests: test_insert_root, test_insert_right_child, test_insert_left_child,test_insert_left_grandchild`, etc.



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