From b9f3ad401188157b46109cfd04c270ff749de7c4 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 4 Mar 2015 13:53:59 -0800 Subject: [PATCH 01/10] add insert and test_bst.py with insert --- bst.py | 35 +++++++++++++++++++++++++++++++++++ test_bst.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 bst.py create mode 100644 test_bst.py diff --git a/bst.py b/bst.py new file mode 100644 index 0000000..2389857 --- /dev/null +++ b/bst.py @@ -0,0 +1,35 @@ +class Bst(object): + """Binary Search Tree""" + def __init__(self, value=None): + self.tree = {} + self.top = None + if not value is None: + self.tree[value] = {'depth': 1} + self.top = value + + def insert(self, value): + current = self.top + if current is None: + self.tree[value] = {'depth': 1} + self.top = value + return + depth = 2 + while not current is None: + print current + if current == value: + return + else: + 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 + return + else: + current = traverse + depth +=1 diff --git a/test_bst.py b/test_bst.py new file mode 100644 index 0000000..97b45b5 --- /dev/null +++ b/test_bst.py @@ -0,0 +1,43 @@ +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 + +@pytest.fixture(scope='function') +def empty_tree(): + tree = bst.Bst() + return tree \ No newline at end of file From 5c44fa06574447038a04f321a6fc2a6608fd1116 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Wed, 4 Mar 2015 13:59:56 -0800 Subject: [PATCH 02/10] Added _size attribute, size(), and test for empty tree. --- bst.py | 9 ++++++++- test_bst.py | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/bst.py b/bst.py index 2389857..ee0edcb 100644 --- a/bst.py +++ b/bst.py @@ -3,9 +3,12 @@ class Bst(object): def __init__(self, value=None): self.tree = {} self.top = None + self._size = 0 + if not value is None: self.tree[value] = {'depth': 1} self.top = value + self._size += 1 def insert(self, value): current = self.top @@ -29,7 +32,11 @@ def insert(self, value): #actual insert self.tree[value] = {'depth': depth} self.tree[current][child] = value + self._size += 1 return else: current = traverse - depth +=1 + depth += 1 + + def size(self): + return self._size \ No newline at end of file diff --git a/test_bst.py b/test_bst.py index 97b45b5..a4ccd66 100644 --- a/test_bst.py +++ b/test_bst.py @@ -37,7 +37,13 @@ def test_insert(empty_tree): 10: {'depth': 2}} assert tree.tree == expect + +def test_size_emptree(): + t = bst.Bst() + assert t.size() == 0 + + @pytest.fixture(scope='function') def empty_tree(): tree = bst.Bst() - return tree \ No newline at end of file + return tree From f9107500d0f00dcc23d5c8c12499a59de7fd9333 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Wed, 4 Mar 2015 14:10:55 -0800 Subject: [PATCH 03/10] Fixed size increment bug that overlooked first/empty case and added test for tree.size() in the case of a filled tree. --- bst.py | 3 ++- test_bst.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/bst.py b/bst.py index ee0edcb..e5e3291 100644 --- a/bst.py +++ b/bst.py @@ -15,10 +15,11 @@ def insert(self, value): if current is None: self.tree[value] = {'depth': 1} self.top = value + self._size += 1 return depth = 2 while not current is None: - print current + # print current if current == value: return else: diff --git a/test_bst.py b/test_bst.py index a4ccd66..b732051 100644 --- a/test_bst.py +++ b/test_bst.py @@ -43,6 +43,21 @@ def test_size_emptree(): assert t.size() == 0 +def test_size_tree(filled_tree): + t = filled_tree + assert t.size() == 15 + + +@pytest.fixture(scope='function') +def filled_tree(): + tree = bst.Bst() + for num in reversed(range(10)): + tree.insert(num) + for num in range(10, 15): + tree.insert(num) + return tree + + @pytest.fixture(scope='function') def empty_tree(): tree = bst.Bst() From 588ca9184445a12474194b17046dba12b46e2c64 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Wed, 4 Mar 2015 14:16:00 -0800 Subject: [PATCH 04/10] Added additional attribute for tracking the depth of a tree, as well as tests for empty and populated trees. --- bst.py | 10 +++++++++- test_bst.py | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/bst.py b/bst.py index e5e3291..4038c0b 100644 --- a/bst.py +++ b/bst.py @@ -4,11 +4,13 @@ def __init__(self, value=None): self.tree = {} self.top = None self._size = 0 + self._depth = 0 if not value is None: self.tree[value] = {'depth': 1} self.top = value self._size += 1 + self._depth += 1 def insert(self, value): current = self.top @@ -16,6 +18,7 @@ def insert(self, value): self.tree[value] = {'depth': 1} self.top = value self._size += 1 + self._depth += 1 return depth = 2 while not current is None: @@ -34,10 +37,15 @@ def insert(self, value): self.tree[value] = {'depth': depth} self.tree[current][child] = value self._size += 1 + if depth > self._depth: + self._depth = depth return else: current = traverse depth += 1 def size(self): - return self._size \ No newline at end of file + return self._size + + def depth(self): + return self._depth \ No newline at end of file diff --git a/test_bst.py b/test_bst.py index b732051..2f7788e 100644 --- a/test_bst.py +++ b/test_bst.py @@ -38,8 +38,8 @@ def test_insert(empty_tree): assert tree.tree == expect -def test_size_emptree(): - t = bst.Bst() +def test_size_emptree(empty_tree): + t = empty_tree assert t.size() == 0 @@ -48,6 +48,16 @@ def test_size_tree(filled_tree): assert t.size() == 15 +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 + + @pytest.fixture(scope='function') def filled_tree(): tree = bst.Bst() From e56c29980451ff1a5f79051458c31ec4c79aa9ed Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 4 Mar 2015 14:22:53 -0800 Subject: [PATCH 05/10] added contains and test for contains --- bst.py | 3 +++ test_bst.py | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/bst.py b/bst.py index 4038c0b..0b48c67 100644 --- a/bst.py +++ b/bst.py @@ -44,6 +44,9 @@ def insert(self, value): current = traverse depth += 1 + def contains(self, value): + return value in self.tree + def size(self): return self._size diff --git a/test_bst.py b/test_bst.py index 2f7788e..bb0d52f 100644 --- a/test_bst.py +++ b/test_bst.py @@ -57,6 +57,15 @@ def test_depth_tree(filled_tree): t = filled_tree assert t.depth() == 10 +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, 15): + 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(): From 5e8601bccea2cadad3700f7e70a58bb6b9c4532b Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 4 Mar 2015 16:11:24 -0800 Subject: [PATCH 06/10] added the ability to generate a graphic representation of a graph using graphviz --- bst.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++--- test_bst.py | 8 +++++++ 2 files changed, 73 insertions(+), 3 deletions(-) mode change 100644 => 100755 bst.py diff --git a/bst.py b/bst.py old mode 100644 new mode 100755 index 0b48c67..6aea93c --- a/bst.py +++ b/bst.py @@ -1,5 +1,10 @@ +#!/usr/bin/env python +import random +import subprocess + + class Bst(object): - """Binary Search Tree""" + """Binary Search Tree using a dictionary""" def __init__(self, value=None): self.tree = {} self.top = None @@ -22,7 +27,6 @@ def insert(self, value): return depth = 2 while not current is None: - # print current if current == value: return else: @@ -44,6 +48,16 @@ def insert(self, value): current = traverse depth += 1 + def balance(self): + """""" + balance = 0 + for i in self.tree: + if self.top > i: + balance -= 1 + elif self.top < i: + balance += 1 + return balance + def contains(self, value): return value in self.tree @@ -51,4 +65,52 @@ def size(self): return self._size def depth(self): - return self._depth \ No newline at end of file + 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(): + """""" + 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", 'test.png'], stdin=subprocess.PIPE) + import pdb; pdb.set_trace() + t.communicate(dot_graph) + +if __name__ == '__main__': + main() diff --git a/test_bst.py b/test_bst.py index bb0d52f..02166c9 100644 --- a/test_bst.py +++ b/test_bst.py @@ -57,6 +57,14 @@ 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() == -4 + def test_contains(filled_tree): t = filled_tree for num in reversed(range(10)): From 25bc1d1a5c6612a83efdafa1a770b892d788637b Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Thu, 5 Mar 2015 09:00:32 -0800 Subject: [PATCH 07/10] added docstrings to functions --- bst.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/bst.py b/bst.py index 6aea93c..ff0e51f 100755 --- a/bst.py +++ b/bst.py @@ -18,6 +18,9 @@ def __init__(self, value=None): 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} @@ -49,7 +52,13 @@ def insert(self, value): depth += 1 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) + + """ balance = 0 for i in self.tree: if self.top > i: @@ -59,12 +68,15 @@ def balance(self): return balance 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): @@ -109,7 +121,6 @@ def main(): tree.insert(num) dot_graph = tree.get_dot() t = subprocess.Popen(["dot", "-Tpng", 'test.png'], stdin=subprocess.PIPE) - import pdb; pdb.set_trace() t.communicate(dot_graph) if __name__ == '__main__': From 1e1b34e7262bf194644ea63ed9b889ae69adda7a Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Thu, 5 Mar 2015 13:36:00 -0800 Subject: [PATCH 08/10] fixed balance and created a better test for it --- test_bst.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test_bst.py b/test_bst.py index 02166c9..2cadfec 100644 --- a/test_bst.py +++ b/test_bst.py @@ -45,7 +45,7 @@ def test_size_emptree(empty_tree): def test_size_tree(filled_tree): t = filled_tree - assert t.size() == 15 + assert t.size() == 14 def test_depth_emptree(empty_tree): @@ -63,13 +63,13 @@ def test_balance(empty_tree, filled_tree): t.insert(3) assert t.balance() == 0 t = filled_tree - assert t.balance() == -4 + 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, 15): + for num in range(10, 14): assert t.contains(num) is True for num in range(20, 25): assert t.contains(num) is False @@ -80,7 +80,7 @@ def filled_tree(): tree = bst.Bst() for num in reversed(range(10)): tree.insert(num) - for num in range(10, 15): + for num in range(10, 14): tree.insert(num) return tree From 8a43f4151a5dbe4a10dccbe22db02686016fb911 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Thu, 5 Mar 2015 13:36:39 -0800 Subject: [PATCH 09/10] fixed balance function --- bst.py | 61 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/bst.py b/bst.py index ff0e51f..df61139 100755 --- a/bst.py +++ b/bst.py @@ -11,7 +11,7 @@ def __init__(self, value=None): self._size = 0 self._depth = 0 - if not value is None: + if value is not None: self.tree[value] = {'depth': 1} self.top = value self._size += 1 @@ -28,28 +28,27 @@ def insert(self, value): self._size += 1 self._depth += 1 return - depth = 2 - while not current is None: + 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: - 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 - else: - current = traverse - depth += 1 + 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: @@ -59,13 +58,16 @@ def balance(self): Return 0 if the tree is balanced (same depth on both sides) """ - balance = 0 - for i in self.tree: - if self.top > i: - balance -= 1 - elif self.top < i: - balance += 1 - return balance + 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.""" @@ -113,15 +115,16 @@ def _get_dot(self, current): 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", 'test.png'], stdin=subprocess.PIPE) + t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) t.communicate(dot_graph) + if __name__ == '__main__': main() From f43ea6e6c3558d5b408ba5a170c5bcba20e420bf Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Thu, 5 Mar 2015 13:42:48 -0800 Subject: [PATCH 10/10] README.md for bst --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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