From b9f3ad401188157b46109cfd04c270ff749de7c4 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 4 Mar 2015 13:53:59 -0800 Subject: [PATCH 01/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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/52] 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 From b524234c439ef8fe73bd9362945d21cbddaf0cec Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 13:46:47 -0800 Subject: [PATCH 11/52] Merge. --- bst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bst.py b/bst.py index 6aea93c..35df399 100755 --- a/bst.py +++ b/bst.py @@ -108,7 +108,7 @@ def main(): 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) import pdb; pdb.set_trace() t.communicate(dot_graph) From 996e65e118a3dabfc1a75d20c9f73050d9a4be9f Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 14:54:16 -0800 Subject: [PATCH 12/52] I understand what I'm getting from a call on in_order() is a generator... --- bst.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/bst.py b/bst.py index 269710d..2cdd739 100755 --- a/bst.py +++ b/bst.py @@ -111,6 +111,16 @@ def _get_dot(self, current): yield "\tnull%s [shape=point];" % r yield "\t%s -> null%s;" % (current, r) + def in_order(self, current='dutch'): + if current == 'dutch': + current = self.top + if current: + for value in self.in_order(self.tree[current].get('left')): + yield value + yield current + for value in self.in_order(self.tree[current].get('right')): + yield value + def main(): """Best case and worst case are the same.""" @@ -119,9 +129,11 @@ def main(): tree.insert(num) for num in range(10, 15): tree.insert(num) + for thing in tree.in_order(): + print thing dot_graph = tree.get_dot() t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) - t.communicate(dot_graph) + # t.communicate(dot_graph) if __name__ == '__main__': From b580d92d0d39e6a3fce8daddb37de7cd85213d1e Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 15:05:52 -0800 Subject: [PATCH 13/52] Fixed generator and added test. --- bst.py | 3 ++- test_bst.py | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/bst.py b/bst.py index 2cdd739..f485d3d 100755 --- a/bst.py +++ b/bst.py @@ -114,7 +114,7 @@ def _get_dot(self, current): def in_order(self, current='dutch'): if current == 'dutch': current = self.top - if current: + if current is not None: for value in self.in_order(self.tree[current].get('left')): yield value yield current @@ -131,6 +131,7 @@ def main(): tree.insert(num) for thing in tree.in_order(): print thing + print tree.tree dot_graph = tree.get_dot() t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) # t.communicate(dot_graph) diff --git a/test_bst.py b/test_bst.py index 2cadfec..7d4860b 100644 --- a/test_bst.py +++ b/test_bst.py @@ -57,6 +57,7 @@ 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 @@ -65,6 +66,7 @@ def test_balance(empty_tree, filled_tree): t = filled_tree assert t.balance() == -5 + def test_contains(filled_tree): t = filled_tree for num in reversed(range(10)): @@ -75,6 +77,13 @@ def test_contains(filled_tree): assert t.contains(num) is False +def test_in_order(filled_tree): + tree = filled_tree + print tree + for place, item in enumerate(tree.in_order()): + assert place == item + + @pytest.fixture(scope='function') def filled_tree(): tree = bst.Bst() From 971d5e5fe64d64f00d5365d254ef95ea1d8fef67 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 16:03:55 -0800 Subject: [PATCH 14/52] Changed in_order() to reflect nature of the recursive call only yielding one value. --- bst.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bst.py b/bst.py index f485d3d..d80643b 100755 --- a/bst.py +++ b/bst.py @@ -111,15 +111,13 @@ def _get_dot(self, current): yield "\tnull%s [shape=point];" % r yield "\t%s -> null%s;" % (current, r) - def in_order(self, current='dutch'): - if current == 'dutch': + def in_order(self, current='start'): + if current == 'start': current = self.top if current is not None: - for value in self.in_order(self.tree[current].get('left')): - yield value + self.in_order(self.tree[current].get('left')).next() yield current - for value in self.in_order(self.tree[current].get('right')): - yield value + self.in_order(self.tree[current].get('right')).next() def main(): From 9e87d91fdcb976bb0d7912cfeedaff760ff5de4b Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Thu, 5 Mar 2015 16:43:54 -0800 Subject: [PATCH 15/52] add breadth first, post_order, and pre_order generators to bst.py and added tests for them --- bst.py | 43 +++++++++++++++++++++++++++++++++++++++++-- test_bst.py | 20 +++++++++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/bst.py b/bst.py index f485d3d..044ecba 100755 --- a/bst.py +++ b/bst.py @@ -111,7 +111,9 @@ def _get_dot(self, current): yield "\tnull%s [shape=point];" % r yield "\t%s -> null%s;" % (current, r) + def in_order(self, current='dutch'): + """Generator that traverses the binary tree.""" if current == 'dutch': current = self.top if current is not None: @@ -122,6 +124,43 @@ def in_order(self, current='dutch'): yield value + def pre_order(self, current='dutch'): + """Generator that traverses the binary tree.""" + if current == 'dutch': + current = self.top + if current is not None: + yield current + for value in self.pre_order(self.tree[current].get('left')): + yield value + for value in self.pre_order(self.tree[current].get('right')): + yield value + + + def post_order(self, current='dutch'): + """Generator that traverses the binary tree.""" + if current == 'dutch': + current = self.top + if current is not None: + for value in self.post_order(self.tree[current].get('left')): + yield value + for value in self.post_order(self.tree[current].get('right')): + yield value + yield current + + def breadth_first(self): + """Generator that traverses the binary tree in breadth first order.""" + node_list =[] + node_list.append(self.top) + current = self.top + while node_list: + current = node_list.pop(0) + if self.tree[current].get('left') is not None: + node_list.append(self.tree[current].get('left')) + if self.tree[current].get('right') is not None: + node_list.append(self.tree[current].get('right')) + yield current + + def main(): """Best case and worst case are the same.""" tree = Bst() @@ -129,9 +168,9 @@ def main(): tree.insert(num) for num in range(10, 15): tree.insert(num) - for thing in tree.in_order(): - print thing print tree.tree + for num in enumerate(tree.breadth_first()): + print num dot_graph = tree.get_dot() t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) # t.communicate(dot_graph) diff --git a/test_bst.py b/test_bst.py index 7d4860b..d2e73ba 100644 --- a/test_bst.py +++ b/test_bst.py @@ -79,11 +79,29 @@ def test_contains(filled_tree): def test_in_order(filled_tree): tree = filled_tree - print tree for place, item in enumerate(tree.in_order()): assert place == item +def test_pre_order(filled_tree): + tree = filled_tree + expected_order = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 10, 11, 12, 13] + for place, item in enumerate(tree.pre_order()): + assert expected_order[place] == item + + +def test_post_order(filled_tree): + tree = filled_tree + expected_order = [0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 12, 11, 10, 9] + for place, item in enumerate(tree.post_order()): + assert expected_order[place] == item + +def test_breadth_first_order(filled_tree): + tree = filled_tree + expected_order = [9, 8, 10, 7, 11, 6, 12, 5, 13, 4, 3, 2, 1, 0] + for place, item in enumerate(tree.breadth_first()): + assert expected_order[place] == item + @pytest.fixture(scope='function') def filled_tree(): tree = bst.Bst() From c36afbc60399f18567befa2b4f8f83a19b67a583 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 16:56:53 -0800 Subject: [PATCH 16/52] Fixed unicode decode error. --- linked_list.py | 5 +++-- test_linked_list.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/linked_list.py b/linked_list.py index 7458b48..a6bc65c 100644 --- a/linked_list.py +++ b/linked_list.py @@ -35,6 +35,8 @@ def insert(self, val): Set .data attribute to val and rearrange the list so the head is the new node with a reference to the old head. """ + # if isinstance(val, unicode): + # val = val.encode('utf-8') self.head = List_Node(val, self.head) self._size += 1 @@ -104,7 +106,7 @@ def display_prep(self): while temp: dummy = temp.data if isinstance(temp.data, str or unicode): - dummy = "'{}'".format(dummy.encode('utf-8')) + dummy = "'{}'".format(dummy) if temp is self.head: output = "{}{}".format(output, dummy) @@ -113,5 +115,4 @@ def display_prep(self): output = "{}, {}".format(output, dummy) temp = temp.next - return output + ")" diff --git a/test_linked_list.py b/test_linked_list.py index e959241..7728aac 100644 --- a/test_linked_list.py +++ b/test_linked_list.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # test constructor from linked_list import List_Node from linked_list import Linked_List From 312b764207b457b87906e015c77571907eecb302 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 17:00:52 -0800 Subject: [PATCH 17/52] Added .travis file for this branch. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bac4e19 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: python +python: + - "2.7" +install: "pip install -r requirements.txt" +script: py.test test_graph.py \ No newline at end of file From 2d88e5acff5648564ec318fc0db064589b75ee4a Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 17:02:39 -0800 Subject: [PATCH 18/52] Added requirements.txt. --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..625ffd1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +py==1.4.26 +pytest==2.6.4 From 37c5e60a1a476dbb4a44db600b9df20558663cf3 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 17:03:46 -0800 Subject: [PATCH 19/52] Updated .yml file. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bac4e19..1019a30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,4 @@ language: python python: - "2.7" install: "pip install -r requirements.txt" -script: py.test test_graph.py \ No newline at end of file +script: py.test test_graph.py test_bst.py \ No newline at end of file From 7691f1328700a2944e8bd7f6350b0849d793849d Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Thu, 5 Mar 2015 17:04:23 -0800 Subject: [PATCH 20/52] updated README.md --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 66d0f71..8f20562 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,14 @@ Code Fellows Dev Accelerator: This repository will be for implementations of classic data structures in python. -parens.py: +## 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'. -bst.py: +## 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 +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. This module includes a Bst class and the following functions: insert, balance, contains, size, depth, get\_dot. It also includes generators that traverse the tree in order: in\_order, pre\_order, post\_order, and breadth\_first. + + + +## Resources: +[Wikipedia Breadth First Search](en.wikipedia.org/wiki/Breadth-first_search) From fd16ce8abfbfd3de0b3b203f06659768f615dfb4 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 5 Mar 2015 17:06:11 -0800 Subject: [PATCH 21/52] Updated to run all tests. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1019a30..5ca17cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,4 @@ language: python python: - "2.7" install: "pip install -r requirements.txt" -script: py.test test_graph.py test_bst.py \ No newline at end of file +script: py.test \ No newline at end of file From 80f6369a4cb883ee5fcfb787b9214ea62a309c29 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Sun, 8 Mar 2015 15:06:17 -0700 Subject: [PATCH 22/52] Changed generators. --- bst.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bst.py b/bst.py index 204c375..4faf9e6 100755 --- a/bst.py +++ b/bst.py @@ -118,9 +118,11 @@ def in_order(self, current='start'): if current == 'start': current = self.top if current is not None: - self.in_order(self.tree[current].get('left')).next() + for node in self.in_order(self.tree[current].get('left')): + yield node yield current - self.in_order(self.tree[current].get('right')).next() + for node in self.in_order(self.tree[current].get('right')): + yield node def pre_order(self, current='dutch'): """Generator that traverses the binary tree.""" @@ -128,17 +130,21 @@ def pre_order(self, current='dutch'): current = self.top if current is not None: yield current - self.in_order(self.tree[current].get('left')).next() - self.in_order(self.tree[current].get('right')).next() + for node in self.in_order(self.tree[current].get('left')): + yield node + for node in self.in_order(self.tree[current].get('right')): + yield node def post_order(self, current='dutch'): """Generator that traverses the binary tree.""" if current == 'dutch': current = self.top if current is not None: - self.in_order(self.tree[current].get('left')).next() + for node in self.in_order(self.tree[current].get('left')): + yield node self.in_order(self.tree[current].get('right')).next() - yield current + for node in self.in_order(self.tree[current].get('right')): + yield node def breadth_first(self): """Generator that traverses the binary tree in breadth first order.""" From 5e7f8676a65e9939d0bc644298f2d8f6e237e93f Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Sun, 8 Mar 2015 18:53:06 -0700 Subject: [PATCH 23/52] added helper functions to bst.py to make functions more clear --- bst.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/bst.py b/bst.py index 204c375..38964bb 100755 --- a/bst.py +++ b/bst.py @@ -17,6 +17,14 @@ def __init__(self, value=None): self._size += 1 self._depth += 1 + def left(self, current): + return self.tree[current].get('left') + + + def right(self, current): + return self.tree[current].get('right') + + def insert(self, value): """Insert a node with value in order. @@ -34,10 +42,10 @@ def insert(self, value): if current == value: return if current < value: - traverse = self.tree[current].get('right') + traverse = self.right(current) child = 'right' else: - traverse = self.tree[current].get('left') + traverse = self.left(current) child = 'left' if traverse is None: #actual insert @@ -118,9 +126,9 @@ def in_order(self, current='start'): if current == 'start': current = self.top if current is not None: - self.in_order(self.tree[current].get('left')).next() + self.in_order(self.left(current)).next() yield current - self.in_order(self.tree[current].get('right')).next() + self.in_order(self.right(current)).next() def pre_order(self, current='dutch'): """Generator that traverses the binary tree.""" @@ -128,16 +136,16 @@ def pre_order(self, current='dutch'): current = self.top if current is not None: yield current - self.in_order(self.tree[current].get('left')).next() - self.in_order(self.tree[current].get('right')).next() + self.in_order(self.left(current)).next() + self.in_order(self.right(current)).next() def post_order(self, current='dutch'): """Generator that traverses the binary tree.""" if current == 'dutch': current = self.top if current is not None: - self.in_order(self.tree[current].get('left')).next() - self.in_order(self.tree[current].get('right')).next() + self.in_order(self.left(current)).next() + self.in_order(self.right(current)).next() yield current def breadth_first(self): @@ -147,10 +155,10 @@ def breadth_first(self): current = self.top while node_list: current = node_list.pop(0) - if self.tree[current].get('left') is not None: - node_list.append(self.tree[current].get('left')) - if self.tree[current].get('right') is not None: - node_list.append(self.tree[current].get('right')) + if self.left(current) is not None: + node_list.append(self.left(current)) + if self.right(current) is not None: + node_list.append(self.right(current)) yield current From aeaba6ebcdb6cb9cf20787ad5da7889822766cf2 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Mon, 9 Mar 2015 12:48:46 -0700 Subject: [PATCH 24/52] tests now look at all values in expected case --- test_bst.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/test_bst.py b/test_bst.py index d2e73ba..016501d 100644 --- a/test_bst.py +++ b/test_bst.py @@ -79,22 +79,33 @@ def test_contains(filled_tree): def test_in_order(filled_tree): tree = filled_tree - for place, item in enumerate(tree.in_order()): - assert place == item + gen = tree.in_order() + expected_order = range(0, 15) + for i in expected_order: + j = gen.next() + print str(i) + '=' + str(j) + assert i == j def test_pre_order(filled_tree): tree = filled_tree + gen = tree.pre_order() expected_order = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 10, 11, 12, 13] - for place, item in enumerate(tree.pre_order()): - assert expected_order[place] == item + for i in expected_order: + j = gen.next() + print str(i) + '=' + str(j) + assert i == j def test_post_order(filled_tree): tree = filled_tree + gen = tree.pre_order() expected_order = [0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 12, 11, 10, 9] - for place, item in enumerate(tree.post_order()): - assert expected_order[place] == item + for i in expected_order: + j = gen.next() + print str(i) + '=' + str(j) + assert i == j + def test_breadth_first_order(filled_tree): tree = filled_tree From a8c6f85750ceb9495c3b7a5ab2be6e3b8cf1c9c0 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Mon, 9 Mar 2015 14:14:38 -0700 Subject: [PATCH 25/52] added a deque to breadth first traverse and fixed in, pre, and post order traversal and fixed the tests --- bst.py | 26 +++++++++++++------------- test_bst.py | 23 ++++++++++++++++++++--- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/bst.py b/bst.py index 232da93..bb6d302 100755 --- a/bst.py +++ b/bst.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import random import subprocess +from collections import deque class Bst(object): @@ -157,31 +158,30 @@ def post_order(self, current='dutch'): def breadth_first(self): """Generator that traverses the binary tree in breadth first order.""" - node_list = [] - node_list.append(self.top) + q1 = deque() + q1.appendleft(self.top) current = self.top - while node_list: - current = node_list.pop(0) + while q1: + current = q1.pop() if self.left(current) is not None: - node_list.append(self.left(current)) + q1.appendleft(self.left(current)) if self.right(current) is not None: - node_list.append(self.right(current)) + q1.appendleft(self.right(current)) yield 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) + inserts = [7, 4, 11, 2, 9, 6, 12, 5, 13, 0, 10, 8, 3, 1] + for i in inserts: + tree.insert(i) print tree.tree - for num in enumerate(tree.pre_order()): - print num + # for num in enumerate(tree.pre_order()): + # print num dot_graph = tree.get_dot() t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) - # t.communicate(dot_graph) + t.communicate(dot_graph) if __name__ == '__main__': diff --git a/test_bst.py b/test_bst.py index 016501d..d1d546c 100644 --- a/test_bst.py +++ b/test_bst.py @@ -80,11 +80,13 @@ def test_contains(filled_tree): def test_in_order(filled_tree): tree = filled_tree gen = tree.in_order() - expected_order = range(0, 15) + expected_order = range(0, 14) for i in expected_order: j = gen.next() print str(i) + '=' + str(j) assert i == j + with pytest.raises(StopIteration): + gen.next() def test_pre_order(filled_tree): @@ -95,16 +97,19 @@ def test_pre_order(filled_tree): j = gen.next() print str(i) + '=' + str(j) assert i == j - + with pytest.raises(StopIteration): + gen.next() def test_post_order(filled_tree): tree = filled_tree - gen = tree.pre_order() + gen = tree.post_order() expected_order = [0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 12, 11, 10, 9] for i in expected_order: j = gen.next() print str(i) + '=' + str(j) assert i == j + with pytest.raises(StopIteration): + gen.next() def test_breadth_first_order(filled_tree): @@ -113,8 +118,10 @@ def test_breadth_first_order(filled_tree): for place, item in enumerate(tree.breadth_first()): assert expected_order[place] == item + @pytest.fixture(scope='function') def filled_tree(): + """Upside down V Shaped Tree""" tree = bst.Bst() for num in reversed(range(10)): tree.insert(num) @@ -123,6 +130,16 @@ def filled_tree(): return tree +@pytest.fixture(scope='function') +def filled_tree_2(): + """Tree with lots of branches""" + inserts = [7, 4, 11, 2, 9, 6, 12, 5, 13, 0, 10, 8, 3, 1] + tree = bst.Bst() + for val in inserts: + tree.insert(val) + return tree + + @pytest.fixture(scope='function') def empty_tree(): tree = bst.Bst() From 7ce920e26664735f9dcea81c13841e331e42a65a Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Mon, 9 Mar 2015 15:04:05 -0700 Subject: [PATCH 26/52] Added reference from children to parents. --- bst.py | 72 +++++++++++++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/bst.py b/bst.py index bb6d302..1b03e3e 100755 --- a/bst.py +++ b/bst.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import random import subprocess -from collections import deque class Bst(object): @@ -18,14 +17,6 @@ def __init__(self, value=None): self._size += 1 self._depth += 1 - def left(self, current): - return self.tree[current].get('left') - - - def right(self, current): - return self.tree[current].get('right') - - def insert(self, value): """Insert a node with value in order. @@ -43,15 +34,18 @@ def insert(self, value): if current == value: return if current < value: - traverse = self.right(current) + traverse = self.tree[current].get('right') child = 'right' else: - traverse = self.left(current) + traverse = self.tree[current].get('left') child = 'left' if traverse is None: - #actual insert + # actual insert self.tree[value] = {'depth': depth} self.tree[current][child] = value + # Add reference to parent in new child node information + self.tree[value]['parent'] = current + self._size += 1 if depth > self._depth: self._depth = depth @@ -122,66 +116,66 @@ def _get_dot(self, current): def in_order(self, current='start'): """ - Generator that traverses the binary tree in order. + Generator that traverses the binary tree. """ if current == 'start': current = self.top if current is not None: - for node in self.in_order(self.left(current)): + for node in self.in_order(self.tree[current].get('left')): yield node yield current - for node in self.in_order(self.right(current)): + for node in self.in_order(self.tree[current].get('right')): yield node - def pre_order(self, current='dutch'): - """Generator that traverses the binary tree pre order.""" + """Generator that traverses the binary tree.""" if current == 'dutch': current = self.top if current is not None: yield current - for node in self.pre_order(self.left(current)): + for node in self.in_order(self.tree[current].get('left')): yield node - for node in self.pre_order(self.right(current)): + for node in self.in_order(self.tree[current].get('right')): yield node def post_order(self, current='dutch'): - """Generator that traverses the binary tree post order.""" + """Generator that traverses the binary tree.""" if current == 'dutch': current = self.top if current is not None: - for node in self.post_order(self.left(current)): - yield node - for node in self.post_order(self.right(current)): + for node in self.in_order(self.tree[current].get('left')): yield node - yield current + self.in_order(self.tree[current].get('right')).next() + for node in self.in_order(self.tree[current].get('right')): + yield node def breadth_first(self): """Generator that traverses the binary tree in breadth first order.""" - q1 = deque() - q1.appendleft(self.top) + node_list =[] + node_list.append(self.top) current = self.top - while q1: - current = q1.pop() - if self.left(current) is not None: - q1.appendleft(self.left(current)) - if self.right(current) is not None: - q1.appendleft(self.right(current)) - yield current + while node_list: + current = node_list.pop(0) + if self.tree[current].get('left') is not None: + node_list.append(self.tree[current].get('left')) + if self.tree[current].get('right') is not None: + node_list.append(self.tree[current].get('right')) + yield current def main(): """Best case and worst case are the same.""" tree = Bst() - inserts = [7, 4, 11, 2, 9, 6, 12, 5, 13, 0, 10, 8, 3, 1] - for i in inserts: - tree.insert(i) + for num in reversed(range(10)): + tree.insert(num) + for num in range(10, 15): + tree.insert(num) print tree.tree - # for num in enumerate(tree.pre_order()): - # print num + for num in enumerate(tree.breadth_first()): + print num dot_graph = tree.get_dot() t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) - t.communicate(dot_graph) + # t.communicate(dot_graph) if __name__ == '__main__': From dbb4e47652913813fe085a3d17240253515e57d3 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Mon, 9 Mar 2015 15:11:00 -0700 Subject: [PATCH 27/52] reverted to older commit because lost changes --- bst.py | 72 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/bst.py b/bst.py index 1b03e3e..bb6d302 100755 --- a/bst.py +++ b/bst.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import random import subprocess +from collections import deque class Bst(object): @@ -17,6 +18,14 @@ def __init__(self, value=None): self._size += 1 self._depth += 1 + def left(self, current): + return self.tree[current].get('left') + + + def right(self, current): + return self.tree[current].get('right') + + def insert(self, value): """Insert a node with value in order. @@ -34,18 +43,15 @@ def insert(self, value): if current == value: return if current < value: - traverse = self.tree[current].get('right') + traverse = self.right(current) child = 'right' else: - traverse = self.tree[current].get('left') + traverse = self.left(current) child = 'left' if traverse is None: - # actual insert + #actual insert self.tree[value] = {'depth': depth} self.tree[current][child] = value - # Add reference to parent in new child node information - self.tree[value]['parent'] = current - self._size += 1 if depth > self._depth: self._depth = depth @@ -116,66 +122,66 @@ def _get_dot(self, current): def in_order(self, current='start'): """ - Generator that traverses the binary tree. + Generator that traverses the binary tree in order. """ if current == 'start': current = self.top if current is not None: - for node in self.in_order(self.tree[current].get('left')): + for node in self.in_order(self.left(current)): yield node yield current - for node in self.in_order(self.tree[current].get('right')): + for node in self.in_order(self.right(current)): yield node + def pre_order(self, current='dutch'): - """Generator that traverses the binary tree.""" + """Generator that traverses the binary tree pre order.""" if current == 'dutch': current = self.top if current is not None: yield current - for node in self.in_order(self.tree[current].get('left')): + for node in self.pre_order(self.left(current)): yield node - for node in self.in_order(self.tree[current].get('right')): + for node in self.pre_order(self.right(current)): yield node def post_order(self, current='dutch'): - """Generator that traverses the binary tree.""" + """Generator that traverses the binary tree post order.""" if current == 'dutch': current = self.top if current is not None: - for node in self.in_order(self.tree[current].get('left')): + for node in self.post_order(self.left(current)): + yield node + for node in self.post_order(self.right(current)): yield node - self.in_order(self.tree[current].get('right')).next() - for node in self.in_order(self.tree[current].get('right')): - yield node + yield current def breadth_first(self): """Generator that traverses the binary tree in breadth first order.""" - node_list =[] - node_list.append(self.top) + q1 = deque() + q1.appendleft(self.top) current = self.top - while node_list: - current = node_list.pop(0) - if self.tree[current].get('left') is not None: - node_list.append(self.tree[current].get('left')) - if self.tree[current].get('right') is not None: - node_list.append(self.tree[current].get('right')) - yield current + while q1: + current = q1.pop() + if self.left(current) is not None: + q1.appendleft(self.left(current)) + if self.right(current) is not None: + q1.appendleft(self.right(current)) + yield 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) + inserts = [7, 4, 11, 2, 9, 6, 12, 5, 13, 0, 10, 8, 3, 1] + for i in inserts: + tree.insert(i) print tree.tree - for num in enumerate(tree.breadth_first()): - print num + # for num in enumerate(tree.pre_order()): + # print num dot_graph = tree.get_dot() t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) - # t.communicate(dot_graph) + t.communicate(dot_graph) if __name__ == '__main__': From f17a5aa3b56be527217d5989b6acdd8beaf881d6 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Mon, 9 Mar 2015 15:12:50 -0700 Subject: [PATCH 28/52] Readded parent information. --- bst.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bst.py b/bst.py index bb6d302..0a44007 100755 --- a/bst.py +++ b/bst.py @@ -52,6 +52,7 @@ def insert(self, value): #actual insert self.tree[value] = {'depth': depth} self.tree[current][child] = value + self.tree[value]['parent'] = current self._size += 1 if depth > self._depth: self._depth = depth From d46308cbfda4aa39620dbe4ddd9b81b020ecfeb2 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Mon, 9 Mar 2015 15:13:56 -0700 Subject: [PATCH 29/52] Added helper function to get a parent for a given node. --- bst.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bst.py b/bst.py index 0a44007..19fd78c 100755 --- a/bst.py +++ b/bst.py @@ -21,10 +21,11 @@ def __init__(self, value=None): def left(self, current): return self.tree[current].get('left') - def right(self, current): return self.tree[current].get('right') + def parent(self, current): + return self.tree[current].get('parent') def insert(self, value): """Insert a node with value in order. @@ -49,7 +50,7 @@ def insert(self, value): traverse = self.left(current) child = 'left' if traverse is None: - #actual insert + # actual insert self.tree[value] = {'depth': depth} self.tree[current][child] = value self.tree[value]['parent'] = current From 28d888a69485837a901e5c3704dd8c2af0b92e38 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Mon, 9 Mar 2015 15:22:40 -0700 Subject: [PATCH 30/52] Added first case for deletion function. --- bst.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bst.py b/bst.py index 19fd78c..a827cfb 100755 --- a/bst.py +++ b/bst.py @@ -60,6 +60,19 @@ def insert(self, value): return current = traverse + def delete(self, val): + pass + + def _delete(self, val, current): + left_of_parent = self.left(self.parent(current)) + right_of_parent = self.right(self.parent(current)) + + if current == val: + if current == left_of_parent: + del self.tree[self.parent(current)]['left'] + else: + del self.tree[self.parent(current)]['right'] + def balance(self): """Returns the balance of the tree: From 88f48ed710d8b0adaa18230f37cc2606ade8800d Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Mon, 9 Mar 2015 15:31:33 -0700 Subject: [PATCH 31/52] Added simple case of deleting node with no children. --- bst.py | 2 +- test_bst.py | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/bst.py b/bst.py index a827cfb..7b9582b 100755 --- a/bst.py +++ b/bst.py @@ -61,7 +61,7 @@ def insert(self, value): current = traverse def delete(self, val): - pass + self._delete(val, val) def _delete(self, val, current): left_of_parent = self.left(self.parent(current)) diff --git a/test_bst.py b/test_bst.py index d1d546c..29473b3 100644 --- a/test_bst.py +++ b/test_bst.py @@ -18,23 +18,29 @@ def test_insert(empty_tree): tree.insert(30) expect = {20: {'depth': 1, 'right': 30}, - 30: {'depth': 2}} + 30: {'depth': 2, + 'parent': 20}} assert tree.tree == expect tree.insert(40) expect = {20: {'depth': 1, 'right': 30}, 30: {'depth': 2, - 'right': 40}, - 40: {'depth': 3}} + 'right': 40, + 'parent': 20}, + 40: {'depth': 3, + 'parent': 30}} 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}} + 'right': 40, + 'parent': 20}, + 40: {'depth': 3, + 'parent': 30}, + 10: {'depth': 2, + 'parent': 20}} assert tree.tree == expect @@ -119,6 +125,13 @@ def test_breadth_first_order(filled_tree): assert expected_order[place] == item +def test_deletion_easy_case(filled_tree_2): + tree = filled_tree_2 + assert tree.left(6) == 5 + tree.delete(5) + assert tree.left(6) is None + + @pytest.fixture(scope='function') def filled_tree(): """Upside down V Shaped Tree""" From 4f1b3ee3e071a390364a6d79cbeae9e61cf119e7 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Mon, 9 Mar 2015 15:38:25 -0700 Subject: [PATCH 32/52] Added tests for deleting nodes with one child, TODO: add assertions for depth. --- test_bst.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test_bst.py b/test_bst.py index 29473b3..5ac3487 100644 --- a/test_bst.py +++ b/test_bst.py @@ -132,6 +132,38 @@ def test_deletion_easy_case(filled_tree_2): assert tree.left(6) is None +def test_deletion_one_left_child_lesser(filled_tree_2): + tree = filled_tree_2 + assert tree.parent(6) == 4 + assert tree.left(6) == 5 + assert tree.right(6) is None + tree.delete(6) + assert tree.left(4) == 2 + assert tree.right(4) == 5 + + assert tree.parent(5) == 4 + assert tree.left(5) is None + assert tree.right(5) is None + + assert tree.contains(6) is False + + +def test_deletion_one_right_child_lesser(filled_tree_2): + tree = filled_tree_2 + assert tree.parent(0) == 2 + assert tree.left(0) is None + assert tree.right(0) == 1 + tree.delete(0) + assert tree.left(2) == 1 + assert tree.right(2) == 3 + + assert tree.parent(1) == 2 + assert tree.left(1) is None + assert tree.right(1) is None + + assert tree.contains(0) is False + + @pytest.fixture(scope='function') def filled_tree(): """Upside down V Shaped Tree""" From 09253c0142e3eddab30997cf0330019f0f2088f2 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Tue, 10 Mar 2015 13:22:49 -0700 Subject: [PATCH 33/52] wrote node_depth funciton and removed node from dictionary. still now working --- bst.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/bst.py b/bst.py index 232da93..a330e18 100755 --- a/bst.py +++ b/bst.py @@ -12,7 +12,7 @@ def __init__(self, value=None): self._depth = 0 if value is not None: - self.tree[value] = {'depth': 1} + self.tree[value] = {} self.top = value self._size += 1 self._depth += 1 @@ -31,7 +31,7 @@ def insert(self, value): In value is already present, it is ignored.""" current = self.top if current is None: - self.tree[value] = {'depth': 1} + self.tree[value] = {} self.top = value self._size += 1 self._depth += 1 @@ -49,7 +49,7 @@ def insert(self, value): child = 'left' if traverse is None: #actual insert - self.tree[value] = {'depth': depth} + self.tree[value] = {} self.tree[current][child] = value self._size += 1 if depth > self._depth: @@ -69,11 +69,11 @@ def balance(self): right_deep = 0 for node, v in self.tree.items(): if self.top > node: - if left_deep < v['depth']: - left_deep = v['depth'] + if left_deep < self.node_depth(node): + left_deep = self.node_depth(node) elif self.top < node: - if right_deep < v['depth']: - right_deep = v['depth'] + if right_deep < self.node_depth(node): + right_deep = self.node_depth(node) return right_deep - left_deep def contains(self, value): @@ -88,6 +88,14 @@ def depth(self): """Returns the depth of the tree.""" return self._depth + def node_depth(self, current): + """Returns the depth of a node in the tree.""" + depth = 0 + while current: + current = current.parent(current) + depth +=1 + return 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 ( From fb638b60728f4f39f8f441f2deeb49dfd08b03fc Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Tue, 10 Mar 2015 13:27:34 -0700 Subject: [PATCH 34/52] took out nodes from trees in test_bst.py --- test_bst.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/test_bst.py b/test_bst.py index 016501d..8546f0d 100644 --- a/test_bst.py +++ b/test_bst.py @@ -7,34 +7,29 @@ def test_Bst(): tree = bst.Bst() assert tree.tree == {} tree = bst.Bst(5) - assert tree.tree == {5: {'depth': 1}} + assert tree.tree == {5: {}} def test_insert(empty_tree): """Test insert into a tree""" tree = empty_tree tree.insert(20) - assert tree.tree == {20: {'depth': 1}} + assert tree.tree == {20: {}} tree.insert(30) - expect = {20: {'depth': 1, - 'right': 30}, - 30: {'depth': 2}} + expect = {20: {'right': 30}, + 30: {}} assert tree.tree == expect tree.insert(40) - expect = {20: {'depth': 1, - 'right': 30}, - 30: {'depth': 2, - 'right': 40}, - 40: {'depth': 3}} + expect = {20: {'right': 30}, + 30: {'right': 40}, + 40: {}} assert tree.tree == expect tree.insert(10) - expect = {20: {'depth': 1, - 'left': 10, + expect = {20: {'left': 10, 'right': 30}, - 30: {'depth': 2, - 'right': 40}, - 40: {'depth': 3}, - 10: {'depth': 2}} + 30: {'right': 40}, + 40: {}, + 10: {}} assert tree.tree == expect From 981ea381318a57462bd8aaa55ba654c678043b12 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Tue, 10 Mar 2015 13:41:59 -0700 Subject: [PATCH 35/52] added test for node_depth --- test_bst.py | 47 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/test_bst.py b/test_bst.py index b73a07c..0d0a758 100644 --- a/test_bst.py +++ b/test_bst.py @@ -16,46 +16,22 @@ def test_insert(empty_tree): tree.insert(20) assert tree.tree == {20: {}} tree.insert(30) -<<<<<<< HEAD expect = {20: {'right': 30}, - 30: {}} + 30: {'parent': 20}} assert tree.tree == expect - tree.insert(40) + tree.insert(40) expect = {20: {'right': 30}, - 30: {'right': 40}, - 40: {}} -======= - expect = {20: {'depth': 1, - 'right': 30}, - 30: {'depth': 2, - 'parent': 20}} - assert tree.tree == expect - tree.insert(40) - expect = {20: {'depth': 1, - 'right': 30}, - 30: {'depth': 2, - 'right': 40, + 30: {'right': 40, 'parent': 20}, - 40: {'depth': 3, - 'parent': 30}} ->>>>>>> 4f1b3ee3e071a390364a6d79cbeae9e61cf119e7 + 40: {'parent': 30}} assert tree.tree == expect tree.insert(10) expect = {20: {'left': 10, 'right': 30}, -<<<<<<< HEAD - 30: {'right': 40}, - 40: {}, - 10: {}} -======= - 30: {'depth': 2, - 'right': 40, + 30: {'right': 40, 'parent': 20}, - 40: {'depth': 3, - 'parent': 30}, - 10: {'depth': 2, - 'parent': 20}} ->>>>>>> 4f1b3ee3e071a390364a6d79cbeae9e61cf119e7 + 40: {'parent': 30}, + 10: {'parent': 20}} assert tree.tree == expect @@ -179,6 +155,15 @@ def test_deletion_one_right_child_lesser(filled_tree_2): assert tree.contains(0) is False +def test_node_depth(filled_tree_2): + assert filled_tree_2.node_depth(7) == 1 + assert filled_tree_2.node_depth(4) == 2 + assert filled_tree_2.node_depth(1) == 5 + assert filled_tree_2.node_depth(13) == 4 + + + + @pytest.fixture(scope='function') def filled_tree(): """Upside down V Shaped Tree""" From a416735f40a3ea21b7b77077991d338a393d2b6c Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Tue, 10 Mar 2015 13:42:36 -0700 Subject: [PATCH 36/52] fixed node_depth so it is correct when a node with value 0 exists --- bst.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bst.py b/bst.py index b8ae74a..02d73b8 100755 --- a/bst.py +++ b/bst.py @@ -108,8 +108,8 @@ def depth(self): def node_depth(self, current): """Returns the depth of a node in the tree.""" depth = 0 - while current: - current = current.parent(current) + while current is not None: + current = self.parent(current) depth +=1 return depth From 635fab23ecc51173ff80fc29ecc4afe479cd5f62 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Tue, 10 Mar 2015 14:00:01 -0700 Subject: [PATCH 37/52] added _rightmost and tests for _rightmost --- bst.py | 30 ++++++++++++++++++++++-------- test_bst.py | 6 +++++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/bst.py b/bst.py index 02d73b8..a8ce37d 100755 --- a/bst.py +++ b/bst.py @@ -62,17 +62,31 @@ def insert(self, value): current = traverse def delete(self, val): - self._delete(val, val) + if self.contains(val): + self._delete(val, val) + def _delete(self, val, current): - left_of_parent = self.left(self.parent(current)) - right_of_parent = self.right(self.parent(current)) + pass - if current == val: - if current == left_of_parent: - del self.tree[self.parent(current)]['left'] - else: - del self.tree[self.parent(current)]['right'] + + def _rightmost(self, start): + """Returns None if start is empty tree""" + while start is not None: + if self.right(start) is None: + return start + start = self.right(start) + + + + # left_of_parent = self.left(self.parent(current)) + # right_of_parent = self.right(self.parent(current)) + + # if current == val: + # if current == left_of_parent: + # del self.tree[self.parent(current)]['left'] + # else: + # del self.tree[self.parent(current)]['right'] def balance(self): """Returns the balance of the tree: diff --git a/test_bst.py b/test_bst.py index 0d0a758..8ce282b 100644 --- a/test_bst.py +++ b/test_bst.py @@ -161,7 +161,11 @@ def test_node_depth(filled_tree_2): assert filled_tree_2.node_depth(1) == 5 assert filled_tree_2.node_depth(13) == 4 - +def test_rightmost(filled_tree_2): + assert filled_tree_2._rightmost(0) == 1 + assert filled_tree_2._rightmost(4) == 6 + assert filled_tree_2._rightmost(7) == 13 + assert filled_tree_2._rightmost(6) == 6 @pytest.fixture(scope='function') From 7f14b3cc36497b5965ed6f6f0174868e0803010c Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Tue, 10 Mar 2015 15:13:42 -0700 Subject: [PATCH 38/52] Added a internal method for swapping nodes. --- bst.py | 42 ++++++++++++++++++++++++++++++++++++++++++ test_bst.py | 14 ++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/bst.py b/bst.py index a8ce37d..a1bb276 100755 --- a/bst.py +++ b/bst.py @@ -70,6 +70,47 @@ def _delete(self, val, current): pass + def _swap_nodes(self, current, target): + """ + Swap two nodes. + + Additionally, update all information on related nodes, as in, children + and parent nodes. + """ + children = [] + + children.append(self.left(current)) + children.append(self.right(current)) + children.append(self.left(target)) + children.append(self.right(target)) + + for child in children: + # If a node children, update it's childrens' information + # for 'swapping' + if child and self.parent(child) == current: + self.tree[child]['parent'] = target + elif child: + self.tree[child]['parent'] = current + + # Swapping information in the parent nodes relative to the nodes being 'swapped' + if current != self.top: + if current == self.tree.get(self.parent(current)).get('left'): + self.tree[self.parent(current)]['left'] = target + else: + self.tree[self.parent(current)]['right'] = target + + if target == self.tree.get(self.parent(target)).get('left'): + self.tree[self.parent(target)]['left'] = current + else: + self.tree[self.parent(target)]['right'] = current + + # Swap current and target's left, right, and parent information + self.tree[current], self.tree[target] = self.tree[target], self.tree[current] + + if current == self.top: + self.top = target + + def _rightmost(self, start): """Returns None if start is empty tree""" while start is not None: @@ -215,6 +256,7 @@ def main(): for i in inserts: tree.insert(i) print tree.tree + tree._swap_nodes(7, 5) # for num in enumerate(tree.pre_order()): # print num dot_graph = tree.get_dot() diff --git a/test_bst.py b/test_bst.py index 8ce282b..a507a15 100644 --- a/test_bst.py +++ b/test_bst.py @@ -116,6 +116,20 @@ def test_breadth_first_order(filled_tree): assert expected_order[place] == item +def test_swap(filled_tree_2): + tree = filled_tree_2 + tree._swap_nodes(7, 5) + assert tree.top == 5 + + assert tree.parent(5) is None + assert tree.left(5) == 4 + assert tree.right(5) == 11 + + assert tree.parent(7) == 6 + assert tree.left(7) is None + assert tree.right(7) is None + + def test_deletion_easy_case(filled_tree_2): tree = filled_tree_2 assert tree.left(6) == 5 From 23d8fa1dd9f40e767e08891adc0356a068317212 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Tue, 10 Mar 2015 15:27:49 -0700 Subject: [PATCH 39/52] fixed swap sot that it stores parents in temp value --- bst.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/bst.py b/bst.py index a1bb276..30db64f 100755 --- a/bst.py +++ b/bst.py @@ -77,6 +77,9 @@ def _swap_nodes(self, current, target): Additionally, update all information on related nodes, as in, children and parent nodes. """ + cur_parent = self.parent(current) + targ_parent = self.parent(target) + children = [] children.append(self.left(current)) @@ -94,15 +97,15 @@ def _swap_nodes(self, current, target): # Swapping information in the parent nodes relative to the nodes being 'swapped' if current != self.top: - if current == self.tree.get(self.parent(current)).get('left'): - self.tree[self.parent(current)]['left'] = target + if current == self.tree.get(cur_parent).get('left'): + self.tree[cur_parent]['left'] = target else: - self.tree[self.parent(current)]['right'] = target + self.tree[cur_parent]['right'] = target - if target == self.tree.get(self.parent(target)).get('left'): - self.tree[self.parent(target)]['left'] = current + if target == self.tree.get(targ_parent).get('left'): + self.tree[targ_parent]['left'] = current else: - self.tree[self.parent(target)]['right'] = current + self.tree[targ_parent]['right'] = current # Swap current and target's left, right, and parent information self.tree[current], self.tree[target] = self.tree[target], self.tree[current] @@ -255,8 +258,9 @@ def main(): inserts = [7, 4, 11, 2, 9, 6, 12, 5, 13, 0, 10, 8, 3, 1] for i in inserts: tree.insert(i) + + tree._swap_nodes(6, 4) print tree.tree - tree._swap_nodes(7, 5) # for num in enumerate(tree.pre_order()): # print num dot_graph = tree.get_dot() From f4c0bb931788c50214bbe1c04d23546bc826c1e9 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Tue, 10 Mar 2015 15:29:40 -0700 Subject: [PATCH 40/52] Added tests for swapping parent/child nodes. --- test_bst.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test_bst.py b/test_bst.py index a507a15..7422be8 100644 --- a/test_bst.py +++ b/test_bst.py @@ -129,6 +129,18 @@ def test_swap(filled_tree_2): assert tree.left(7) is None assert tree.right(7) is None +def test_swap_parentchild(filled_tree_2): + tree = filled_tree_2 + tree._swap_nodes(7, 4) + assert tree.top == 4 + + assert tree.parent(4) is None + assert tree.left(4) == 7 + assert tree.right(4) == 11 + + assert tree.parent(7) == 4 + assert tree.left(7) is 2 + assert tree.right(7) is 6 def test_deletion_easy_case(filled_tree_2): tree = filled_tree_2 From 7cd9ed22f968a8c0fe93cc393445e1678f03820a Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Tue, 10 Mar 2015 15:34:22 -0700 Subject: [PATCH 41/52] Added parent references to dot graph for tree. --- bst.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bst.py b/bst.py index 30db64f..07a5eca 100755 --- a/bst.py +++ b/bst.py @@ -83,7 +83,7 @@ def _swap_nodes(self, current, target): children = [] children.append(self.left(current)) - children.append(self.right(current)) + children.append(self.right(current))x` children.append(self.left(target)) children.append(self.right(target)) @@ -185,6 +185,10 @@ 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') + parent = self.tree[current].get('parent') + if parent is not None: + yield "\t%s -> %s;" % (current, parent) + if left is not None: yield "\t%s -> %s;" % (current, left) for i in self._get_dot(left): @@ -202,6 +206,7 @@ def _get_dot(self, current): yield "\tnull%s [shape=point];" % r yield "\t%s -> null%s;" % (current, r) + def in_order(self, current='start'): """ Generator that traverses the binary tree in order. From 1d9f4604951e5f261e9d43b6d9f56a04c24cebd8 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Tue, 10 Mar 2015 15:57:34 -0700 Subject: [PATCH 42/52] Added delete for simple case of no children, and recursive case for presence of a left child. --- bst.py | 27 +++++++++++++++++++++------ test_bst.py | 1 + 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/bst.py b/bst.py index 07a5eca..963668f 100755 --- a/bst.py +++ b/bst.py @@ -63,11 +63,26 @@ def insert(self, value): def delete(self, val): if self.contains(val): - self._delete(val, val) - - - def _delete(self, val, current): - pass + left_child = self.left(val) + right_child = self.right(val) + parent = self.parent(val) + if left_child is None and right_child is None: + if parent: + if self.left(parent) == val: + del self.tree[parent]['left'] + else: + del self.tree[parent]['right'] + del self.tree[val] + print + print 'deletion' + print self.tree + print + elif left_child is not None: + biggest_child = self._rightmost(left_child) + self._swap_nodes(val, biggest_child) + print self.tree + print + self.delete(val) def _swap_nodes(self, current, target): @@ -83,7 +98,7 @@ def _swap_nodes(self, current, target): children = [] children.append(self.left(current)) - children.append(self.right(current))x` + children.append(self.right(current)) children.append(self.left(target)) children.append(self.right(target)) diff --git a/test_bst.py b/test_bst.py index 7422be8..e6ff7ea 100644 --- a/test_bst.py +++ b/test_bst.py @@ -147,6 +147,7 @@ def test_deletion_easy_case(filled_tree_2): assert tree.left(6) == 5 tree.delete(5) assert tree.left(6) is None + assert tree.contains(5) is False def test_deletion_one_left_child_lesser(filled_tree_2): From be37e9c50031435f7b86910862fac34052376d18 Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Tue, 10 Mar 2015 16:14:06 -0700 Subject: [PATCH 43/52] Fixed case of deleting a node that is on the left of it's parent, but only has a right child. --- bst.py | 23 ++++++++++++++++------- test_bst.py | 1 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/bst.py b/bst.py index 963668f..08b352b 100755 --- a/bst.py +++ b/bst.py @@ -67,22 +67,31 @@ def delete(self, val): right_child = self.right(val) parent = self.parent(val) if left_child is None and right_child is None: - if parent: + # Delete a node with not children + if parent is not None: + # For a node with a parent... if self.left(parent) == val: del self.tree[parent]['left'] else: del self.tree[parent]['right'] + else: + # For deleting a node that is the root with no children + self.top = None del self.tree[val] - print - print 'deletion' - print self.tree - print elif left_child is not None: biggest_child = self._rightmost(left_child) self._swap_nodes(val, biggest_child) - print self.tree - print self.delete(val) + else: + if parent is not None: + if self.left(parent) == val: + self.tree[parent]['left'] = right_child + else: + self.tree[parent]['right'] = right_child + self.tree[right_child]['parent'] = parent + else: + self.top = right_child + del self.tree[val] def _swap_nodes(self, current, target): diff --git a/test_bst.py b/test_bst.py index e6ff7ea..ee0ba17 100644 --- a/test_bst.py +++ b/test_bst.py @@ -172,6 +172,7 @@ def test_deletion_one_right_child_lesser(filled_tree_2): assert tree.left(0) is None assert tree.right(0) == 1 tree.delete(0) + assert tree.left(2) == 1 assert tree.right(2) == 3 From f4673ad9a7bb8d0f7970902b7f894fcddfd42686 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Thu, 12 Mar 2015 19:58:40 -0700 Subject: [PATCH 44/52] wrote get, set, and heap in hash.py --- hash.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 hash.py diff --git a/hash.py b/hash.py new file mode 100644 index 0000000..6f372cb --- /dev/null +++ b/hash.py @@ -0,0 +1,25 @@ + + +class Heap(object): + def __init__(self, bins): + self.heap = [] + for i in range(0, bins): + self.heap.append([]) + + def set(self, key, val): + """store the given val using the given key""" + tup = (key, val) + self.heap[self.hash(key)].apppend(tup) + + def hash(self, key): + sum = 0 + for letter in key: + sum += ord(letter) + return sum % len(self.heap) + + def get(self, key): + """return the value stored with the given key""" + bucket = self.heap[self.hash(key)] + for element in bucket: + if element[0] == key: + return element[1] From ccc63d4fbea1d898cf539ed627f945e4572b47cd Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 12 Mar 2015 20:15:22 -0700 Subject: [PATCH 45/52] Added basic tests for hash. --- hash.py | 16 +++++++--------- test_hash.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 test_hash.py diff --git a/hash.py b/hash.py index 6f372cb..396edbe 100644 --- a/hash.py +++ b/hash.py @@ -1,21 +1,19 @@ - - -class Heap(object): - def __init__(self, bins): +class Hash(object): + def __init__(self, buckets): self.heap = [] - for i in range(0, bins): + for i in range(0, buckets): self.heap.append([]) def set(self, key, val): """store the given val using the given key""" tup = (key, val) - self.heap[self.hash(key)].apppend(tup) + self.heap[self.hash(key)].append(tup) def hash(self, key): - sum = 0 + total = 0 for letter in key: - sum += ord(letter) - return sum % len(self.heap) + total += ord(letter) + return total % len(self.heap) def get(self, key): """return the value stored with the given key""" diff --git a/test_hash.py b/test_hash.py new file mode 100644 index 0000000..c8046b3 --- /dev/null +++ b/test_hash.py @@ -0,0 +1,35 @@ +from hash import Hash +import pytest + + +def test_create(): + something = Hash(10) + assert isinstance(something.heap, list) + assert len(something.heap) == 10 + + +def test_bad_create(): + with pytest.raises(TypeError): + something = Hash() + + +def test_hash(): + something = Hash(10) + hashed_val = something.hash('hello') + assert hashed_val == 2 + + +def test_set(): + something = Hash(10) + something.set('asdf', 10) + + sum = 0 + for char in 'asdf': + sum += ord(char) + assert ('asdf', 10) in something.heap[sum % 10] + + +def test_get(): + something = Hash(10) + something.set('asdf', 10) + assert something.get('asdf') == 10 From 32caadb5ebd563692798e6cc7e1c258a00ceb034 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Thu, 12 Mar 2015 20:32:13 -0700 Subject: [PATCH 46/52] added test for UNIX word dictionary --- test_hash.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test_hash.py b/test_hash.py index c8046b3..f184a50 100644 --- a/test_hash.py +++ b/test_hash.py @@ -1,6 +1,6 @@ from hash import Hash import pytest - +from io import open def test_create(): something = Hash(10) @@ -33,3 +33,16 @@ def test_get(): something = Hash(10) something.set('asdf', 10) assert something.get('asdf') == 10 + +def test_on_word(): + """testing on word dictionary built into UNIX""" + infile = open('/usr/share/dict/words', 'r') + full_text = [] + for line in infile: + full_text.append(line.strip()) + allbins = Hash(10000) + for word in full_text: + allbins.set(word, word) + for word in full_text: + assert allbins.get(word) == word + From 786f79dd64dd42b387c243f605129ffcfb387f95 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 1 Apr 2015 21:02:08 -0700 Subject: [PATCH 47/52] changed list to h_list to clarify name --- hash.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hash.py b/hash.py index 396edbe..69296c7 100644 --- a/hash.py +++ b/hash.py @@ -1,23 +1,23 @@ class Hash(object): def __init__(self, buckets): - self.heap = [] + self.h_list = [] for i in range(0, buckets): - self.heap.append([]) + self.h_list.append([]) def set(self, key, val): """store the given val using the given key""" tup = (key, val) - self.heap[self.hash(key)].append(tup) + self.h_list[self.hash(key)].append(tup) def hash(self, key): total = 0 for letter in key: total += ord(letter) - return total % len(self.heap) + return total % len(self.h_list) def get(self, key): """return the value stored with the given key""" - bucket = self.heap[self.hash(key)] + bucket = self.h_list[self.hash(key)] for element in bucket: if element[0] == key: return element[1] From dd8d073ddfa9c89afdf9acc0ffb2b5741b8fbe20 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 1 Apr 2015 21:29:04 -0700 Subject: [PATCH 48/52] added a test for duplicate keys put into hash table --- test_hash.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/test_hash.py b/test_hash.py index f184a50..4bc726b 100644 --- a/test_hash.py +++ b/test_hash.py @@ -4,8 +4,8 @@ def test_create(): something = Hash(10) - assert isinstance(something.heap, list) - assert len(something.heap) == 10 + assert isinstance(something.h_list, list) + assert len(something.h_list) == 10 def test_bad_create(): @@ -13,6 +13,20 @@ def test_bad_create(): something = Hash() +# def test_not_string(): +# something = Hash(10) + +def test_duplicate_key_different_value(): + """a get on a hash table should return the last value put in""" + hash_table = Hash(10) + word = 'asd' + values = range(4) + for value in values: + hash_table.set(word, value) + # last value in was 3 + assert hash_table.get(word) == 3 + + def test_hash(): something = Hash(10) hashed_val = something.hash('hello') @@ -26,7 +40,7 @@ def test_set(): sum = 0 for char in 'asdf': sum += ord(char) - assert ('asdf', 10) in something.heap[sum % 10] + assert ('asdf', 10) in something.h_list[sum % 10] def test_get(): @@ -41,8 +55,8 @@ def test_on_word(): for line in infile: full_text.append(line.strip()) allbins = Hash(10000) - for word in full_text: - allbins.set(word, word) - for word in full_text: - assert allbins.get(word) == word + # for word in full_text: + # allbins.set(word, word) + # for word in full_text: + # assert allbins.get(word) == word From 7b0f126f2aeb1fdfd7d2c437fa5489f025ca7cdb Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 1 Apr 2015 21:39:35 -0700 Subject: [PATCH 49/52] wrote two tests for using a non-sting as a key --- test_hash.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test_hash.py b/test_hash.py index 4bc726b..59b0162 100644 --- a/test_hash.py +++ b/test_hash.py @@ -13,8 +13,20 @@ def test_bad_create(): something = Hash() -# def test_not_string(): -# something = Hash(10) + +def test_set_return_error(): + hash_table = Hash(10) + word = 123 + with pytest.raises(TypeError): + hash_table.set(word, word) + + +def test_set_return_error(): + hash_table = Hash(10) + word = [1, 2, 3] + with pytest.raises(TypeError): + hash_table.set(word, word) + def test_duplicate_key_different_value(): """a get on a hash table should return the last value put in""" From f877187e593b3d18de799531ee5158af3f5ae472 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 1 Apr 2015 21:44:59 -0700 Subject: [PATCH 50/52] added a nicer error raised from using something of the wrong type --- hash.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hash.py b/hash.py index 69296c7..b234f95 100644 --- a/hash.py +++ b/hash.py @@ -10,6 +10,9 @@ def set(self, key, val): self.h_list[self.hash(key)].append(tup) def hash(self, key): + if not isinstance(key, str): + type_ = type(key) + raise TypeError('Cannot hash something of type {}'.format(type_)) total = 0 for letter in key: total += ord(letter) From 81dd743e9bf6825970bb9146f7352396f3e16128 Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 1 Apr 2015 22:17:24 -0700 Subject: [PATCH 51/52] rewrote set so that it replaces a list item when setting with a duplicate key --- hash.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hash.py b/hash.py index b234f95..1c5b5d8 100644 --- a/hash.py +++ b/hash.py @@ -7,7 +7,12 @@ def __init__(self, buckets): def set(self, key, val): """store the given val using the given key""" tup = (key, val) - self.h_list[self.hash(key)].append(tup) + bucket = self.h_list[self.hash(key)] + for item in bucket: + if item[0] == key: + bucket.remove(item) + break + bucket.append(tup) def hash(self, key): if not isinstance(key, str): From c52fefeaa31ede5fdb24633bc3e2bb9792ee881c Mon Sep 17 00:00:00 2001 From: Henry Grantham Date: Wed, 1 Apr 2015 22:36:29 -0700 Subject: [PATCH 52/52] added test to make sure that one one key stored for duplicate keys --- hash.py | 7 +++++-- test_hash.py | 29 +++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/hash.py b/hash.py index 1c5b5d8..8923163 100644 --- a/hash.py +++ b/hash.py @@ -5,7 +5,10 @@ def __init__(self, buckets): self.h_list.append([]) def set(self, key, val): - """store the given val using the given key""" + """Store the given val using the given key. + + Replaces the value if setting with a key already in. + """ tup = (key, val) bucket = self.h_list[self.hash(key)] for item in bucket: @@ -15,7 +18,7 @@ def set(self, key, val): bucket.append(tup) def hash(self, key): - if not isinstance(key, str): + if not isinstance(key, (str, unicode)): type_ = type(key) raise TypeError('Cannot hash something of type {}'.format(type_)) total = 0 diff --git a/test_hash.py b/test_hash.py index 59b0162..444bd5a 100644 --- a/test_hash.py +++ b/test_hash.py @@ -13,20 +13,19 @@ def test_bad_create(): something = Hash() - -def test_set_return_error(): +def test_set_integer_return_error(): hash_table = Hash(10) word = 123 with pytest.raises(TypeError): hash_table.set(word, word) -def test_set_return_error(): +def test_set_list_return_error(): hash_table = Hash(10) word = [1, 2, 3] with pytest.raises(TypeError): hash_table.set(word, word) - + def test_duplicate_key_different_value(): """a get on a hash table should return the last value put in""" @@ -39,6 +38,19 @@ def test_duplicate_key_different_value(): assert hash_table.get(word) == 3 +def test_duplicate_key_different_value_in_only_once(): + """a set on a hash table with duplicate keys should only put one in""" + hash_table = Hash(10) + word = 'asd' + values = range(4) + for value in values: + hash_table.set(word, value) + sum = 0 + for char in word: + sum += ord(char) + # should only be one value in bucket + assert len(hash_table.h_list[sum % 10]) == 1 + def test_hash(): something = Hash(10) hashed_val = something.hash('hello') @@ -60,6 +72,7 @@ def test_get(): something.set('asdf', 10) assert something.get('asdf') == 10 + def test_on_word(): """testing on word dictionary built into UNIX""" infile = open('/usr/share/dict/words', 'r') @@ -67,8 +80,8 @@ def test_on_word(): for line in infile: full_text.append(line.strip()) allbins = Hash(10000) - # for word in full_text: - # allbins.set(word, word) - # for word in full_text: - # assert allbins.get(word) == word + for word in full_text: + allbins.set(word, word) + for word in full_text: + assert allbins.get(word) == word