From 1d1a5e93c9fd37fe2e7bbe4ecfe795d5335cedfe Mon Sep 17 00:00:00 2001 From: Ted Callahan Date: Mon, 16 Jan 2017 12:44:25 -0800 Subject: [PATCH 01/87] Initial build out of node and bst structures. --- src/bst.py | 32 ++++++++++++++++++++++++++++++++ src/test_bst.py | 1 + 2 files changed, 33 insertions(+) create mode 100644 src/bst.py create mode 100644 src/test_bst.py diff --git a/src/bst.py b/src/bst.py new file mode 100644 index 0000000..ba9ab44 --- /dev/null +++ b/src/bst.py @@ -0,0 +1,32 @@ +"""This module implements a binary search tree.""" + +class Node(object): + """Node object for use in a binary search tree.""" + def __init__(self): + self.key = key + self.value = value + self.right = right + self.left = left + + + + +class BinarySearchTree(object): + """Binary Search Tree Object. + + Methods: + - insert(self, val): will insert the value val into the BST. If val is already present, it will be ignored. + - search(self, val): will return the node containing that value, else None + - size(self): will return the integer size of the BST (equal to the total number of values stored in the tree). It will return 0 if the tree is empty. + - depth(self): will return an integer representing the total number of levels in the tree. If there is one value, the depth should be 1, if two values it will be 2, if three values it may be 2 or three, depending, etc. + - contains(self, val): will return True if val is in the BST, False if not. + - balance(self): will return an integer, positive or negative that represents how well balanced the tree is. Trees which are higher on the left than the right should return a positive value, trees which are higher on the right than the left should return a negative value. An ideally-balanced tree should return 0. + + """ + + def __init__(self): + """Initialize a Binary Search Tree object.""" + pass + + def insert(self, val): + \ No newline at end of file diff --git a/src/test_bst.py b/src/test_bst.py new file mode 100644 index 0000000..2f32ae3 --- /dev/null +++ b/src/test_bst.py @@ -0,0 +1 @@ +"""This module tests a binary search tree.""" From 6f6a938c70d6641944b7ddadadc36cd657cddc89 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 16 Jan 2017 13:37:18 -0800 Subject: [PATCH 02/87] Added bst and test_bst files. --- src/bst.py | 1 + src/test_bst.py | 1 + 2 files changed, 2 insertions(+) create mode 100644 src/bst.py create mode 100644 src/test_bst.py diff --git a/src/bst.py b/src/bst.py new file mode 100644 index 0000000..300550c --- /dev/null +++ b/src/bst.py @@ -0,0 +1 @@ +"""Module for Binary Search Tree.""" diff --git a/src/test_bst.py b/src/test_bst.py new file mode 100644 index 0000000..4e39447 --- /dev/null +++ b/src/test_bst.py @@ -0,0 +1 @@ +"""Test Module for Binary Search Tree.""" \ No newline at end of file From 98c5039f1beb4911c0cf2a4fae5becc0c43ddd82 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 16 Jan 2017 14:03:22 -0800 Subject: [PATCH 03/87] commiting initial insert tests. --- src/test_bst.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/test_bst.py b/src/test_bst.py index 2b8ca81..d6dbe35 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -1 +1,30 @@ """Test Module for Binary Search Tree.""" + +def test_insert1(): + a = bst() + a.insert(5) + assert a[5] + +def test_insert2(): + a = bst() + a.insert(5) + a.insert(10) + assert a[5] == ["z", 10] + +def test_insert3(): + a = bst() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a[6] == [4, 7] + assert a[3] == [1, 6] + assert a[14] == [13, "z"] + assert a[8] == [3, 10] + assert a[13] == ["z", "z"] + From 0ecf7b2390632e399030028033f7c0cccbce0789 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 16 Jan 2017 14:30:42 -0800 Subject: [PATCH 04/87] added insert method. --- src/bst.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index 6d07b52..22babec 100644 --- a/src/bst.py +++ b/src/bst.py @@ -1,2 +1,29 @@ +"""Module for Binary Search Tree.""" -"""Module for Binary Search Tree.""" \ No newline at end of file + +class BinarySearchTree(object): + """Foo.""" + + def __init__(self): + """Init of the Binary Search Tree class.""" + self._bstdict = {} + self.root = None + self.vertex = self.root + + def insert(self, val): + """Takes a value, inserts into Binary Search Tree at correct placement.""" + if not any(self._bstdict): + self.root = val + self._bstdict[val] = ["z", "z"] + + elif val > self._bstdict[self.vertex]: + if self._bstdict[self.vertex][1] == "z": + self._bstdict[self.vertex][1] = val + self.vertex = self.vertex[1] + insert(val) + + elif val < self._bstdict[self.vertex]: + if self._bstdict[self.vertex][0] == "z": + self._bstdict[self.vertex][0] = val + self.vertex = self.vertex[0] + insert(val) From 597732fee731503a4f13c0f797a9072534411256 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 16 Jan 2017 15:02:43 -0800 Subject: [PATCH 05/87] commit to pull remote changes. --- src/test_bst.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test_bst.py b/src/test_bst.py index d6dbe35..c118ea9 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -27,4 +27,3 @@ def test_insert3(): assert a[14] == [13, "z"] assert a[8] == [3, 10] assert a[13] == ["z", "z"] - From 5c46d3b379a2a890c667b698a4c8c138ef8b6a4d Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 16 Jan 2017 15:07:08 -0800 Subject: [PATCH 06/87] refactor of insert and tests. --- src/bst.py | 6 +++--- src/test_bst.py | 51 +++++++++++++++++++++++++++---------------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/bst.py b/src/bst.py index 22babec..4f190b3 100644 --- a/src/bst.py +++ b/src/bst.py @@ -14,16 +14,16 @@ def insert(self, val): """Takes a value, inserts into Binary Search Tree at correct placement.""" if not any(self._bstdict): self.root = val - self._bstdict[val] = ["z", "z"] + self._bstdict[val] = [None, None] elif val > self._bstdict[self.vertex]: - if self._bstdict[self.vertex][1] == "z": + if self._bstdict[self.vertex][1] == None: self._bstdict[self.vertex][1] = val self.vertex = self.vertex[1] insert(val) elif val < self._bstdict[self.vertex]: - if self._bstdict[self.vertex][0] == "z": + if self._bstdict[self.vertex][0] == None: self._bstdict[self.vertex][0] = val self.vertex = self.vertex[0] insert(val) diff --git a/src/test_bst.py b/src/test_bst.py index d6dbe35..6de1791 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -1,30 +1,35 @@ """Test Module for Binary Search Tree.""" -def test_insert1(): - a = bst() - a.insert(5) - assert a[5] + +# def test_insert1(): +# from bst import BinarySearchTree +# # import pdb; pdb.set_trace() +# a = BinarySearchTree() +# a.insert(5) +# assert a._bstdict[5] def test_insert2(): - a = bst() + from bst import BinarySearchTree + a = BinarySearchTree() + import pdb; pdb.set_trace() a.insert(5) a.insert(10) - assert a[5] == ["z", 10] - -def test_insert3(): - a = bst() - a.insert(8) - a.insert(10) - a.insert(3) - a.insert(14) - a.insert(13) - a.insert(1) - a.insert(6) - a.insert(7) - a.insert(4) - assert a[6] == [4, 7] - assert a[3] == [1, 6] - assert a[14] == [13, "z"] - assert a[8] == [3, 10] - assert a[13] == ["z", "z"] + assert a._bstdict[5] == [None, 10] +# def test_insert3(): +# from bst import BinarySearchTree +# a = BinarySearchTree() +# a.insert(8) +# a.insert(10) +# a.insert(3) +# a.insert(14) +# a.insert(13) +# a.insert(1) +# a.insert(6) +# a.insert(7) +# a.insert(4) +# assert a._bstdict[6] == [4, 7] +# # assert a[3] == [1, 6] +# # assert a[14] == [13, "z"] +# # assert a[8] == [3, 10] +# # assert a[13] == ["z", "z"] From c5587dcf3f36dd1e84619593b760288a521996ff Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 16 Jan 2017 16:03:23 -0800 Subject: [PATCH 07/87] fixed initial tests and insert function, new test for size. --- src/bst.py | 64 +++++++++++++++++++++++++++++++++------------ src/test_bst.py | 69 ++++++++++++++++++++++++++++++------------------- 2 files changed, 89 insertions(+), 44 deletions(-) diff --git a/src/bst.py b/src/bst.py index 4f190b3..ae0858c 100644 --- a/src/bst.py +++ b/src/bst.py @@ -1,29 +1,59 @@ """Module for Binary Search Tree.""" +class Node(object): + + def __init__(self, value=None, left=None, right=None): + self.value = value + self.left = left + self.right = right + + class BinarySearchTree(object): """Foo.""" + """insert(self, val): will insert the value val into the BST. If val is already present, it will be ignored.""" + """search(self, val): will return the node containing that value, else None""" + """size(self): will return the integer size of the BST (equal to the total number of values stored in the tree). It will return 0 if the tree is empty.""" + """depth(self): will return an integer representing the total number of levels in the tree. If there is one value, the depth should be 1, if two values it will be 2, if three values it may be 2 or three, depending, etc.""" + """contains(self, val): will return True if val is in the BST, False if not.""" + """balance(self): will return an integer, positive or negative that represents how well balanced the tree is. Trees which are higher on the left than the right should return a positive value, trees which are higher on the right than the left should return a negative value. An ideally-balanced tree should return 0.""" + + def __init__(self): """Init of the Binary Search Tree class.""" - self._bstdict = {} self.root = None - self.vertex = self.root + self.counter = 0 def insert(self, val): """Takes a value, inserts into Binary Search Tree at correct placement.""" - if not any(self._bstdict): - self.root = val - self._bstdict[val] = [None, None] - - elif val > self._bstdict[self.vertex]: - if self._bstdict[self.vertex][1] == None: - self._bstdict[self.vertex][1] = val - self.vertex = self.vertex[1] - insert(val) - - elif val < self._bstdict[self.vertex]: - if self._bstdict[self.vertex][0] == None: - self._bstdict[self.vertex][0] = val - self.vertex = self.vertex[0] - insert(val) + if self.root is None: + self.root = Node(val) + self.counter += 1 + + else: + vertex = self.root + while True: + if val > vertex.value: + if vertex.right: + vertex = vertex.right + else: + vertex.right = Node(val) + self.counter += 1 + break + + elif val < vertex.value: + if vertex.left: + vertex = vertex.left + else: + vertex.left = Node(val) + self.counter += 1 + break + + + # def search(self, val): + + + def size(self): + """Returns size of Binary Search Tree.""" + diff --git a/src/test_bst.py b/src/test_bst.py index 6de1791..c2a8aea 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -1,35 +1,50 @@ """Test Module for Binary Search Tree.""" +from bst import BinarySearchTree -# def test_insert1(): -# from bst import BinarySearchTree -# # import pdb; pdb.set_trace() -# a = BinarySearchTree() -# a.insert(5) -# assert a._bstdict[5] +def test_insert_5_is_root(): + a = BinarySearchTree() + a.insert(5) + assert a.root + +def test_insert_5_where_root_equals_5(): + a = BinarySearchTree() + a.insert(5) + assert a.root.value == 5 -def test_insert2(): - from bst import BinarySearchTree +def test_insert_5_and_10_and_confirm_right(): a = BinarySearchTree() - import pdb; pdb.set_trace() a.insert(5) a.insert(10) - assert a._bstdict[5] == [None, 10] + assert a.root.right.value == 10 -# def test_insert3(): -# from bst import BinarySearchTree -# a = BinarySearchTree() -# a.insert(8) -# a.insert(10) -# a.insert(3) -# a.insert(14) -# a.insert(13) -# a.insert(1) -# a.insert(6) -# a.insert(7) -# a.insert(4) -# assert a._bstdict[6] == [4, 7] -# # assert a[3] == [1, 6] -# # assert a[14] == [13, "z"] -# # assert a[8] == [3, 10] -# # assert a[13] == ["z", "z"] +def test_insert_many_numbers(): + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.root.right.right.left.value == 13 + assert a.root.left.value == 3 + assert a.root.right.right.value == 14 + assert a.root.value == 8 + assert a.root.left.right.left.value == 4 + +def test_size_returns_size_of_binary_search_tree(): + """Test that the size method returns size of the bst.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.size() == 9 From 994d3cb2b86586c0fbbcf53ad88d8b5325d6435c Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 16 Jan 2017 16:03:50 -0800 Subject: [PATCH 08/87] commit to pull remote changes. --- src/bst.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bst.py b/src/bst.py index 4f190b3..8e87bf7 100644 --- a/src/bst.py +++ b/src/bst.py @@ -12,6 +12,7 @@ def __init__(self): def insert(self, val): """Takes a value, inserts into Binary Search Tree at correct placement.""" + import pdb; pdb.set_trace() if not any(self._bstdict): self.root = val self._bstdict[val] = [None, None] From afecfc5f6a3d34d7e17557c8c9ecf7cbf109ca6c Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Tue, 17 Jan 2017 09:13:50 -0800 Subject: [PATCH 09/87] else to handle if node with value already exists on insert. --- src/bst.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bst.py b/src/bst.py index d960111..826351b 100644 --- a/src/bst.py +++ b/src/bst.py @@ -52,6 +52,8 @@ def insert(self, val): self.counter += 1 self.container.append(val) break + else: + break def size(self): """Return size of Binary Search Tree.""" From f407dc325d637c0803d55ffca1e9b8cc6c26cd0f Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Tue, 17 Jan 2017 09:51:29 -0800 Subject: [PATCH 10/87] edited docstrings. --- src/bst.py | 8 ++++-- src/test_bst.py | 74 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/src/bst.py b/src/bst.py index 826351b..c4b1949 100644 --- a/src/bst.py +++ b/src/bst.py @@ -2,15 +2,17 @@ class Node(object): + """Node class.""" def __init__(self, value=None, left=None, right=None): + """Init of the Node class.""" self.value = value self.left = left self.right = right class BinarySearchTree(object): - """Foo.""" + """Binary Search Tree.""" """insert(self, val): will insert the value val into the BST. If val is already present, it will be ignored.""" """search(self, val): will return the node containing that value, else None""" @@ -82,6 +84,7 @@ def search(self, val): def depth(self): """ Return an integer representing the total number of levels in the tree. + If there is one value, the depth should be 1, if two values it will be 2, if three values it may be 2 or three, depending, etc. """ @@ -97,10 +100,11 @@ def calc_depth(self, tree): def balance(self): """ Return an integer, positive or negative that represents how well balanced the tree is. + Trees which are higher on the left than the right should return a positive value, trees which are higher on the right than the left should return a negative value. An ideally-balanced tree should return 0. """ if self.root is None: return 0 - return self.calc_depth(self.root.left) - self.calc_depth(self.root.right) + return self.calc_depth(self.root.right) - self.calc_depth(self.root.left) diff --git a/src/test_bst.py b/src/test_bst.py index e950d9b..eff9834 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -2,23 +2,30 @@ from bst import BinarySearchTree -def test_insert_5_is_root(): +def test_insert_5_is_root(): + """Test the insert function.""" a = BinarySearchTree() a.insert(5) assert a.root + def test_insert_5_where_root_equals_5(): + """Test the insert funciton.""" a = BinarySearchTree() a.insert(5) assert a.root.value == 5 + def test_insert_5_and_10_and_confirm_right(): + """Test the insert function.""" a = BinarySearchTree() a.insert(5) a.insert(10) assert a.root.right.value == 10 + def test_insert_many_numbers(): + """Test the insert function.""" a = BinarySearchTree() a.insert(8) a.insert(10) @@ -35,6 +42,7 @@ def test_insert_many_numbers(): assert a.root.value == 8 assert a.root.left.right.left.value == 4 + def test_size_returns_size_of_binary_search_tree(): """Test that the size method returns size of the bst.""" a = BinarySearchTree() @@ -49,6 +57,7 @@ def test_size_returns_size_of_binary_search_tree(): a.insert(4) assert a.size() == 9 + def test_binary_search_tree_contains_value(): """Test that the contains method returns True if value in binary search tree.""" a = BinarySearchTree() @@ -63,6 +72,7 @@ def test_binary_search_tree_contains_value(): a.insert(4) assert a.contains(4) + def test_binary_search_tree_does_not_contain_value(): """Test that the contains method returns True if value in binary search tree.""" a = BinarySearchTree() @@ -77,22 +87,58 @@ def test_binary_search_tree_does_not_contain_value(): a.insert(4) assert a.contains(100) is False + def test_search_5(): + """Test the search function.""" a = BinarySearchTree() a.insert(5) assert a.search(5) == a.root + def test_search_10(): + """Test the search function.""" a = BinarySearchTree() a.insert(5) a.insert(10) assert a.search(10) == a.root.right + def test_search_empty(): + """Test the search function.""" a = BinarySearchTree() assert a.search(5) is None -# def test_search_none(): + +def test_search_none(): + """Test the search function.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.search(100) is None + + +# def test_depth_zero(): +# """Test the depth function.""" +# a = BinarySearchTree() +# assert a.depth == 0 + + +# def test_depth_one(): +# """Test the depth function.""" +# a = BinarySearchTree() +# a.insert(8) +# assert a.depth == 1 + + +# def test_depth_many(): +# """Test the depth function.""" # a = BinarySearchTree() # a.insert(8) # a.insert(10) @@ -103,14 +149,11 @@ def test_search_empty(): # a.insert(6) # a.insert(7) # a.insert(4) -# assert a.search(100) is None +# assert a.depth == 4 -# def test_depth_one(): -# a = BinarySearchTree() -# a.insert(8) -# assert a.depth == 1 -# def test_depth_many(): +# def test_balance(): +# """Test the balance function.""" # a = BinarySearchTree() # a.insert(8) # a.insert(10) @@ -120,17 +163,4 @@ def test_search_empty(): # a.insert(1) # a.insert(6) # a.insert(7) -# a.insert(4) -# assert a.depth == 4 - -def test_balance(): - a = BinarySearchTree() - a.insert(8) - a.insert(10) - a.insert(3) - a.insert(14) - a.insert(13) - a.insert(1) - a.insert(6) - a.insert(7) - assert a.balance == 1 +# assert a.balance == -1 From c86b4f27db9c70fddad376aaae589063f16bf0d5 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Tue, 17 Jan 2017 13:40:29 -0800 Subject: [PATCH 11/87] bst class can take an iter, built test fixture to fill bst. --- src/bst.py | 14 +++++++++++++- src/test_bst.py | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index c4b1949..c8f6246 100644 --- a/src/bst.py +++ b/src/bst.py @@ -20,12 +20,24 @@ class BinarySearchTree(object): """depth(self): will return an integer representing the total number of levels in the tree. If there is one value, the depth should be 1, if two values it will be 2, if three values it may be 2 or three, depending, etc.""" """contains(self, val): will return True if val is in the BST, False if not.""" """balance(self): will return an integer, positive or negative that represents how well balanced the tree is. Trees which are higher on the left than the right should return a positive value, trees which are higher on the right than the left should return a negative value. An ideally-balanced tree should return 0.""" + """in_order(self): will return a generator that will return the values in the tree using in-order traversal, one at a time.""" + """pre_order(self): will return a generator that will return the values in the tree using pre-order traversal, one at a time.""" + """post_order(self): will return a generator that will return the values in the tree using post_order traversal, one at a time.""" + """breadth_first(self): will return a generator that will return the values in the tree using breadth-first traversal, one at a time.""" - def __init__(self): + + def __init__(self, if_iter=None): """Init of the Binary Search Tree class.""" self.root = None self.counter = 0 self.container = [] + if if_iter: + try: + for value in if_iter: + self.insert(value) + except TypeError: + self.push(if_iter) + def insert(self, val): """Take a value, inserts into Binary Search Tree at correct placement.""" diff --git a/src/test_bst.py b/src/test_bst.py index eff9834..99873c9 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -1,6 +1,16 @@ """Test Module for Binary Search Tree.""" from bst import BinarySearchTree +BST_SIMPLE = [8, 10, 3, 14, 13, 1, 6, 7, 4] + + +@pytest.fixture +def filled_bst(): + """Fixture to fill the bst tree with nodes.""" + from bst import BinarySearchTree + new_tree = BinarySearchTree(BST_SIMPLE) + return new_tree + def test_insert_5_is_root(): """Test the insert function.""" @@ -164,3 +174,8 @@ def test_search_none(): # a.insert(6) # a.insert(7) # assert a.balance == -1 + + +def test_in_order_traversal(): + """Returns a generator that returns in-order traversal results.""" + \ No newline at end of file From 584ed8e1cd4c83b08f8fa1317a882503520aa2eb Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Tue, 17 Jan 2017 16:38:41 -0800 Subject: [PATCH 12/87] added in_order function, refactor one test. --- src/bst.py | 16 +++++++++++++++- src/test_bst.py | 7 ++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/bst.py b/src/bst.py index c8f6246..e283e9c 100644 --- a/src/bst.py +++ b/src/bst.py @@ -36,7 +36,7 @@ def __init__(self, if_iter=None): for value in if_iter: self.insert(value) except TypeError: - self.push(if_iter) + self.insert(if_iter) def insert(self, val): @@ -120,3 +120,17 @@ def balance(self): if self.root is None: return 0 return self.calc_depth(self.root.right) - self.calc_depth(self.root.left) + + def in_order(self): + """Traverse in_order, yielding via generator.""" + vertex = self.root + visited = [] + while (not any(visited) or vertex is not None): + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + yield vertex.value + vertex = vertex.right + diff --git a/src/test_bst.py b/src/test_bst.py index 99873c9..27c0a08 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -1,5 +1,6 @@ """Test Module for Binary Search Tree.""" from bst import BinarySearchTree +import pytest BST_SIMPLE = [8, 10, 3, 14, 13, 1, 6, 7, 4] @@ -176,6 +177,6 @@ def test_search_none(): # assert a.balance == -1 -def test_in_order_traversal(): - """Returns a generator that returns in-order traversal results.""" - \ No newline at end of file +def test_in_order_traversal_first_node_traversed_is_1(filled_bst): + """In-order traversal will get """ + assert next(filled_bst.in_order()) == 1 From 97c15734c3fe4c8d3ec41c8f062e0621137bb997 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Tue, 17 Jan 2017 16:39:13 -0800 Subject: [PATCH 13/87] recursive function. --- src/bst.py | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/src/bst.py b/src/bst.py index c8f6246..d8791a6 100644 --- a/src/bst.py +++ b/src/bst.py @@ -25,7 +25,6 @@ class BinarySearchTree(object): """post_order(self): will return a generator that will return the values in the tree using post_order traversal, one at a time.""" """breadth_first(self): will return a generator that will return the values in the tree using breadth-first traversal, one at a time.""" - def __init__(self, if_iter=None): """Init of the Binary Search Tree class.""" self.root = None @@ -37,7 +36,8 @@ def __init__(self, if_iter=None): self.insert(value) except TypeError: self.push(if_iter) - + self._in_order = self.in_order_trav(self.root) + self.collect = [] def insert(self, val): """Take a value, inserts into Binary Search Tree at correct placement.""" @@ -120,3 +120,73 @@ def balance(self): if self.root is None: return 0 return self.calc_depth(self.root.right) - self.calc_depth(self.root.left) + + def in_order(self): + """Return.""" + return next(self._in_order) + + def recursive(self, node): + """Return.""" + print('before t.l', node.left, node.left.value) + if node.left: + print('in t.l') + yield next(self.recursive(node.left)) + self.recursive(node.left) + print('after rec') + print('hey', node.left, node.value) + self.collect.append(node.value) + print('before yield', node.value) + yield node.value + print('after yield', node.right) + if node.right: + self.recursive(node.right) + # print(node.value) + + def pre_order(self): + """Return.""" + num = 0 + while num < 10: + yield num + num += 1 + + def post_order(self): + """Return.""" + num = 0 + while num < 10: + yield num + num += 1 + + def breadth_first(self): + """Return.""" + num = 0 + while num < 10: + yield num + num += 1 + + def in_order_trav(self, tree): + """Return.""" + import pdb; pdb.set_trace() + if tree is None: + return + if tree.left: + for each in self.in_order_trav(tree.left): + yield self.in_order_trav(tree.left) + yield tree.value + if tree.right: + for each in self.in_order_trav(tree.right): + yield tree.value + + # for i in self.in_order_trav(tree.left): + # if tree.left is not None: + # yield tree.left.value + # yield None + # if tree.left: + # for i in self.in_order_trav(tree.left): + # yield tree.value + # yield tree.value + # if tree is None: + # yield + # num = 0 + # while num < 10: + # yield num + # num += 1 From aba9fa0db2526503773eebe6a5171f3bd4df7da5 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Tue, 17 Jan 2017 16:55:03 -0800 Subject: [PATCH 14/87] tests --- src/bst.py | 3 +-- src/test_bst.py | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bst.py b/src/bst.py index e283e9c..5489247 100644 --- a/src/bst.py +++ b/src/bst.py @@ -125,7 +125,7 @@ def in_order(self): """Traverse in_order, yielding via generator.""" vertex = self.root visited = [] - while (not any(visited) or vertex is not None): + while visited if vertex is not None: visited.append(vertex) vertex = vertex.left @@ -133,4 +133,3 @@ def in_order(self): vertex = visited.pop() yield vertex.value vertex = vertex.right - diff --git a/src/test_bst.py b/src/test_bst.py index 27c0a08..a83ea05 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -179,4 +179,7 @@ def test_search_none(): def test_in_order_traversal_first_node_traversed_is_1(filled_bst): """In-order traversal will get """ - assert next(filled_bst.in_order()) == 1 + in_order_list = [] + for x in filled_bst.in_order(): + in_order_list.append(x) + assert in_order_list[0] == 1 From 01174068a414e5882329919471cf62084b63b7b4 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Wed, 18 Jan 2017 09:47:02 -0800 Subject: [PATCH 15/87] pre order traversal, beginning post order traversal. --- src/bst.py | 35 ++++++++++++++++++++ src/test_bst.py | 85 ++++++++++++++++++++++++++----------------------- 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/src/bst.py b/src/bst.py index 1241abc..0a8a6f0 100644 --- a/src/bst.py +++ b/src/bst.py @@ -37,6 +37,8 @@ def __init__(self, if_iter=None): except TypeError: self.insert(if_iter) self._in_order = self.in_order_trav() + self._pre_order = self.pre_order_trav() + self._post_order = self.post_order_trav() def insert(self, val): """Take a value, inserts into Binary Search Tree at correct placement.""" @@ -137,6 +139,39 @@ def in_order_trav(self): yield vertex.value vertex = vertex.right + def pre_order(self): + """Return.""" + return next(self._pre_order) + + def pre_order_trav(self): + """Traverse pre_order, yielding via generator.""" + vertex = self.root + visited = [] + while (visited or vertex is not None): + if vertex is not None: + yield vertex.value + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + vertex = vertex.right + + def post_order(self): + """Return.""" + return next(self._post_order) + + def post_order_trav(self): + """Traverse pre_order, yielding via generator.""" + vertex = self.root + visited = [] + while (visited or vertex is not None): + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + vertex = vertex.right + # """Return.""" diff --git a/src/test_bst.py b/src/test_bst.py index fb8ef39..3f48a1f 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -135,48 +135,53 @@ def test_search_none(): assert a.search(100) is None -# def test_depth_zero(): -# """Test the depth function.""" -# a = BinarySearchTree() -# assert a.depth == 0 - - -# def test_depth_one(): -# """Test the depth function.""" -# a = BinarySearchTree() -# a.insert(8) -# assert a.depth == 1 - - -# def test_depth_many(): -# """Test the depth function.""" -# a = BinarySearchTree() -# a.insert(8) -# a.insert(10) -# a.insert(3) -# a.insert(14) -# a.insert(13) -# a.insert(1) -# a.insert(6) -# a.insert(7) -# a.insert(4) -# assert a.depth == 4 - - -# def test_balance(): -# """Test the balance function.""" -# a = BinarySearchTree() -# a.insert(8) -# a.insert(10) -# a.insert(3) -# a.insert(14) -# a.insert(13) -# a.insert(1) -# a.insert(6) -# a.insert(7) -# assert a.balance == -1 +def test_depth_zero(): + """Test the depth function.""" + a = BinarySearchTree() + assert a.depth() == 0 + + +def test_depth_one(): + """Test the depth function.""" + a = BinarySearchTree() + a.insert(8) + assert a.depth() == 1 + + +def test_depth_many(): + """Test the depth function.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + a.insert(4) + assert a.depth() == 4 + + +def test_balance(): + """Test the balance function.""" + a = BinarySearchTree() + a.insert(8) + a.insert(10) + a.insert(3) + a.insert(14) + a.insert(13) + a.insert(1) + a.insert(6) + a.insert(7) + assert a.balance() == 0 def test_in_order_traversal_first_node_traversed_is_1(filled_bst): """In-order traversal will get """ assert filled_bst.in_order() == 1 + + +def test_pre_order_traversal_first_node_traversed_is_1(filled_bst): + """Pre-order traversal will get """ + assert filled_bst.pre_order() == 8 From dde70aeef8509a4e6ba9ccfc5b96e8e9bbc60f7a Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Wed, 18 Jan 2017 14:07:44 -0800 Subject: [PATCH 16/87] commit to pull post_order trav. --- src/bst.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bst.py b/src/bst.py index 0a8a6f0..5962f05 100644 --- a/src/bst.py +++ b/src/bst.py @@ -173,6 +173,7 @@ def post_order_trav(self): vertex = vertex.right + # """Return.""" # def recursive(self, node): From f7f2aba6ccaba43973d2f04042d92da956983ab9 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Wed, 18 Jan 2017 14:09:25 -0800 Subject: [PATCH 17/87] trying again, post order. --- src/bst.py | 11 +++++++++-- src/test_bst.py | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/bst.py b/src/bst.py index e27a1bd..f67ab7d 100644 --- a/src/bst.py +++ b/src/bst.py @@ -39,6 +39,7 @@ def __init__(self, if_iter=None): self._in_order = self.in_order_trav() self._pre_order = self.pre_order_trav() self._post_order = self.post_order_trav() + self._breadth_first = self.breadth_first_trav() def insert(self, val): """Take a value, inserts into Binary Search Tree at correct placement.""" @@ -175,11 +176,17 @@ def post_order_trav(self): peek_vertex = visited[-1] if peek_vertex.right and peek_vertex.right is not last_vertex: vertex = peek_vertex.right - yield vertex.value else: - visited.append(peek_vertex) + yield peek_vertex.value last_vertex = visited.pop() + def breadth_first(self): + """Fill in later.""" + return next(self._breadth_first) + + def breadth_first_trav(self): + """Traverse breadth first order, yielding a generator.""" + # """Return.""" diff --git a/src/test_bst.py b/src/test_bst.py index 5de76a7..7097d5b 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -193,3 +193,7 @@ def test_in_order_traversal_first_node_traversed_is_1(filled_bst): def test_pre_order_traversal_first_node_traversed_is_1(filled_bst): """Pre-order traversal will get """ assert filled_bst.pre_order() == 8 + +def test_post_order_traversal(filled_bst): + """Post-order traversal.""" + \ No newline at end of file From ba81221b738ae47abf7725bfbe1f741772dac813 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Wed, 18 Jan 2017 14:10:24 -0800 Subject: [PATCH 18/87] added one space. --- src/bst.py | 70 ------------------------------------------------------ 1 file changed, 70 deletions(-) diff --git a/src/bst.py b/src/bst.py index 0ba40db..b702153 100644 --- a/src/bst.py +++ b/src/bst.py @@ -179,73 +179,3 @@ def post_order_trav(self): else: visited.append(peek_vertex) last_vertex = visited.pop() - - - -# """Return.""" - -# def recursive(self, node): -# """Return.""" -# print('before t.l', node.left, node.left.value) -# if node.left: -# print('in t.l') -# yield next(self.recursive(node.left)) -# self.recursive(node.left) -# print('after rec') -# print('hey', node.left, node.value) -# self.collect.append(node.value) -# print('before yield', node.value) -# yield node.value -# print('after yield', node.right) -# if node.right: -# self.recursive(node.right) -# # print(node.value) - -# def pre_order(self): -# """Return.""" -# num = 0 -# while num < 10: -# yield num -# num += 1 - -# def post_order(self): -# """Return.""" -# num = 0 -# while num < 10: -# yield num -# num += 1 - -# def breadth_first(self): -# """Return.""" -# num = 0 -# while num < 10: -# yield num -# num += 1 - -# def in_order_trav(self, tree): -# """Return.""" -# import pdb; pdb.set_trace() -# if tree is None: -# return -# if tree.left: -# for each in self.in_order_trav(tree.left): -# yield self.in_order_trav(tree.left) -# yield tree.value -# if tree.right: -# for each in self.in_order_trav(tree.right): -# yield tree.value - -# # for i in self.in_order_trav(tree.left): -# # if tree.left is not None: -# # yield tree.left.value -# # yield None -# # if tree.left: -# # for i in self.in_order_trav(tree.left): -# # yield tree.value -# # yield tree.value -# # if tree is None: -# # yield -# # num = 0 -# # while num < 10: -# # yield num -# # num += 1 From a8ed6460cbdba24335c7b21af25c39b7e7e5e6b5 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 09:52:46 -0800 Subject: [PATCH 19/87] beggining implementation of delete node function. --- src/bst.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/bst.py b/src/bst.py index f8840bb..17d692f 100644 --- a/src/bst.py +++ b/src/bst.py @@ -197,3 +197,97 @@ def breadth_first_trav(self): q.enqueue(vertex.left) if (vertex.right): q.enqueue(vertex.right) + + def delete(self, val): + """Remove val from the tree if present, if not present this method is a no-op. Return None in all cases.""" + vertex = self.root + parent_of_del = None + del_node = None + if self.root.value == val: + left = self.root.left + right = self.root.right + self.root = self._find_min(self.root) + self.root.left = left + self.root.right = right + else: + while True: + if vertex.right and val == vertex.right.value: + parent_of_del = vertex + del_node = parent_of_del.right + break + elif vertex.left and val == vertex.left.value: + parent_of_del = vertex + del_node = parent_of_del.left + break + elif val > vertex.value and vertex.right: + vertex = vertex.right + elif val < vertex.value and vertex.left: + vertex = vertex.left + else: + return + min_parent = self._find_min_parent(parent_of_del) + if val == parent_of_del.right.value: + del_node = parent_of_del.right + left = del_node.left + right = del_node.right + min_node = min_parent.left + + if min_node.right: + min_parent.left = min_node.right + del_node = min_node + del_node.right = right + del_node.left = left + parent_of_del.right = del_node + + elif val == parent_of_del.left.value: + del_node = parent_of_del.left + left = del_node.left + right = del_node.right + min_node = min_parent.left + + if min_node.right: + min_parent.left = min_node.right + del_node = min_node + del_node.right = right + del_node.left = left + parent_of_del.right = del_node + + parent_of_del + + return + + def _find_min_parent(self, parent_of_del): + """Find the parent of the leftmost node on the right of the node you wish to delete.""" + + + # self._find_min(parent_of_del) + + + + # while vertex: + # if val > vertex.value: + # if not vertex.right: + # return None + # vertex = vertex.right + # elif val < vertex.value: + # if not vertex.left: + # return None + # vertex = vertex.left + # else: + # return vertex + # return None + + # del_node = self._find_min(vertex, val, del_node) + + + + # def _find_min(self, vertex, val, del_node): + # if vertex.right: + # vertex = vertex.right + # while True: + # if vertex.left == val: + # vertex.left.right = del_node.right + # vertex.left.left = del_node.left + # if vertex.left.right: + # vertex.left = vertex.left.right + # return vertex From 1800d6da72a0810bcc7128f85e2102eec17e9dfe Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Thu, 19 Jan 2017 13:59:20 -0800 Subject: [PATCH 20/87] finished, for now, delete function. --- src/bst.py | 69 ++++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/src/bst.py b/src/bst.py index 17d692f..14b5832 100644 --- a/src/bst.py +++ b/src/bst.py @@ -206,7 +206,7 @@ def delete(self, val): if self.root.value == val: left = self.root.left right = self.root.right - self.root = self._find_min(self.root) + self.root = self._find_min_parent(self.root).left self.root.left = left self.root.right = right else: @@ -214,10 +214,12 @@ def delete(self, val): if vertex.right and val == vertex.right.value: parent_of_del = vertex del_node = parent_of_del.right + min_parent = self._find_min_parent(parent_of_del, "right") break elif vertex.left and val == vertex.left.value: parent_of_del = vertex del_node = parent_of_del.left + min_parent = self._find_min_parent(parent_of_del, "left") break elif val > vertex.value and vertex.right: vertex = vertex.right @@ -225,7 +227,7 @@ def delete(self, val): vertex = vertex.left else: return - min_parent = self._find_min_parent(parent_of_del) + if val == parent_of_del.right.value: del_node = parent_of_del.right left = del_node.left @@ -252,42 +254,31 @@ def delete(self, val): del_node.left = left parent_of_del.right = del_node - parent_of_del + def _find_min_parent(self, vertex, side): + """Find the parent of the replacement node, given the parent of the delete node.""" + if side == "right": + if not vertex.right.right and not vertex.right.left: + return + if vertex.right.right and not vertex.right.right.left: + return vertex.right + elif vertex.right.right and vertex.right.right.left: + vertex = vertex.right.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left + else: + if not vertex.left.right and not vertex.left.left: + return + if vertex.left.right and not vertex.left.right.left: + return vertex.left + elif vertex.left.right and vertex.left.right.left: + vertex = vertex.left.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left return - - def _find_min_parent(self, parent_of_del): - """Find the parent of the leftmost node on the right of the node you wish to delete.""" - - - # self._find_min(parent_of_del) - - - - # while vertex: - # if val > vertex.value: - # if not vertex.right: - # return None - # vertex = vertex.right - # elif val < vertex.value: - # if not vertex.left: - # return None - # vertex = vertex.left - # else: - # return vertex - # return None - - # del_node = self._find_min(vertex, val, del_node) - - - - # def _find_min(self, vertex, val, del_node): - # if vertex.right: - # vertex = vertex.right - # while True: - # if vertex.left == val: - # vertex.left.right = del_node.right - # vertex.left.left = del_node.left - # if vertex.left.right: - # vertex.left = vertex.left.right - # return vertex From 6a06c4503cf0680b16ddfba11f11ca20bd661e97 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 14:02:17 -0800 Subject: [PATCH 21/87] initial refactor of delete. --- src/bst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index 17d692f..c86388c 100644 --- a/src/bst.py +++ b/src/bst.py @@ -206,7 +206,7 @@ def delete(self, val): if self.root.value == val: left = self.root.left right = self.root.right - self.root = self._find_min(self.root) + self.root = self._find_min_parent(self.root).left self.root.left = left self.root.right = right else: From 110c919477731b7788b53e0cb6f93a41a7ee33f0 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 14:53:48 -0800 Subject: [PATCH 22/87] added edge case catches for self.root holding value to delete. --- src/bst.py | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/bst.py b/src/bst.py index 14b5832..90815ca 100644 --- a/src/bst.py +++ b/src/bst.py @@ -203,12 +203,25 @@ def delete(self, val): vertex = self.root parent_of_del = None del_node = None + if self.root is None: + return if self.root.value == val: left = self.root.left right = self.root.right - self.root = self._find_min_parent(self.root).left + if not right: + self.root = self.root.left + self.counter -= 1 + return + if not right.left: + self.root = right + self.root.left = left + self.counter -= 1 + return + min_node = self._find_min_parent(self.root, "right").left + self.root = min_node self.root.left = left self.root.right = right + self.counter -= 1 else: while True: if vertex.right and val == vertex.right.value: @@ -229,11 +242,19 @@ def delete(self, val): return if val == parent_of_del.right.value: + if not min_parent: + parent_of_del.right = None + self.counter -= 1 + return del_node = parent_of_del.right + if min_parent is del_node: + right = del_node.right + min_parent.right = right + self.counter -= 1 + return left = del_node.left right = del_node.right min_node = min_parent.left - if min_node.right: min_parent.left = min_node.right del_node = min_node @@ -242,20 +263,36 @@ def delete(self, val): parent_of_del.right = del_node elif val == parent_of_del.left.value: + if not min_parent: + parent_of_del.left = None + self.counter -= 1 + return del_node = parent_of_del.left + if min_parent is del_node: + left = del_node.right + parent_of_del.left = left + self.counter -= 1 + return left = del_node.left right = del_node.right min_node = min_parent.left - if min_node.right: min_parent.left = min_node.right del_node = min_node del_node.right = right del_node.left = left parent_of_del.right = del_node + self.counter -= 1 def _find_min_parent(self, vertex, side): """Find the parent of the replacement node, given the parent of the delete node.""" + if vertex is self.root: + vertex = vertex.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left if side == "right": if not vertex.right.right and not vertex.right.left: return @@ -280,5 +317,4 @@ def _find_min_parent(self, vertex, side): return vertex else: vertex = vertex.left - return From 8bab894beeb92764d07c3500597172c63817ca83 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 15:00:53 -0800 Subject: [PATCH 23/87] delete bug fix for parent of del node del node on right. --- src/bst.py | 2 +- src/test_bst.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/bst.py b/src/bst.py index 90815ca..0e7f144 100644 --- a/src/bst.py +++ b/src/bst.py @@ -281,7 +281,7 @@ def delete(self, val): del_node = min_node del_node.right = right del_node.left = left - parent_of_del.right = del_node + parent_of_del.left = del_node self.counter -= 1 def _find_min_parent(self, vertex, side): diff --git a/src/test_bst.py b/src/test_bst.py index 7097d5b..de90d92 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -196,4 +196,9 @@ def test_pre_order_traversal_first_node_traversed_is_1(filled_bst): def test_post_order_traversal(filled_bst): """Post-order traversal.""" - \ No newline at end of file + + +def test_delete_empty(): + """Test delete function with empty binary search tree.""" + a = BinarySearchTree() + From 0e37d23df868476e19c1fde200744012403a2b20 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 15:23:06 -0800 Subject: [PATCH 24/87] fixed bug in find_min_parent helper function of delete. --- src/bst.py | 14 ++++++-------- src/test_bst.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/bst.py b/src/bst.py index 0e7f144..1eb0b14 100644 --- a/src/bst.py +++ b/src/bst.py @@ -217,7 +217,12 @@ def delete(self, val): self.root.left = left self.counter -= 1 return - min_node = self._find_min_parent(self.root, "right").left + vertex = vertex.right + while True: + if not vertex.left.left: + min_node = vertex + else: + vertex = vertex.left self.root = min_node self.root.left = left self.root.right = right @@ -286,13 +291,6 @@ def delete(self, val): def _find_min_parent(self, vertex, side): """Find the parent of the replacement node, given the parent of the delete node.""" - if vertex is self.root: - vertex = vertex.right - while True: - if not vertex.left.left: - return vertex - else: - vertex = vertex.left if side == "right": if not vertex.right.right and not vertex.right.left: return diff --git a/src/test_bst.py b/src/test_bst.py index de90d92..71d7d43 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -194,6 +194,7 @@ def test_pre_order_traversal_first_node_traversed_is_1(filled_bst): """Pre-order traversal will get """ assert filled_bst.pre_order() == 8 + def test_post_order_traversal(filled_bst): """Post-order traversal.""" @@ -201,4 +202,36 @@ def test_post_order_traversal(filled_bst): def test_delete_empty(): """Test delete function with empty binary search tree.""" a = BinarySearchTree() - + assert a.delete(5) is None + + +def test_delete_filled_root(filled_bst): + """Test delete of root.""" + a = filled_bst + assert a.size() == 9 + a.delete(8) + assert a.size() == 8 + assert a.in_order() == 1 + assert a.in_order() == 3 + assert a.in_order() == 4 + assert a.in_order() == 6 + assert a.in_order() == 7 + assert a.in_order() == 10 + assert a.in_order() == 13 + assert a.in_order() == 14 + + +def test_delete_end_root(filled_bst): + """Test delete of root.""" + a = filled_bst + assert a.size() == 9 + a.delete(1) + assert a.size() == 8 + assert a.in_order() == 3 + assert a.in_order() == 4 + assert a.in_order() == 6 + assert a.in_order() == 7 + assert a.in_order() == 8 + assert a.in_order() == 10 + assert a.in_order() == 13 + assert a.in_order() == 14 From 515d19eb70128035ba3c919e903cd2c30eb080af Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 15:29:01 -0800 Subject: [PATCH 25/87] root removal root right has left min node has right. --- src/bst.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index 1eb0b14..71a850f 100644 --- a/src/bst.py +++ b/src/bst.py @@ -220,9 +220,12 @@ def delete(self, val): vertex = vertex.right while True: if not vertex.left.left: - min_node = vertex + min_parent = vertex else: vertex = vertex.left + min_node = min_parent.left + if min_node.right: + min_parent.left = min_node.right self.root = min_node self.root.left = left self.root.right = right From 204e0dd777b29a0cce4de331b1549c1925c0bd19 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 15:41:19 -0800 Subject: [PATCH 26/87] refactored contains function to cooperate with delete. --- src/bst.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index 71a850f..daceae1 100644 --- a/src/bst.py +++ b/src/bst.py @@ -79,7 +79,10 @@ def size(self): def contains(self, val): """Return True if val is in the BST, False if not.""" - return val in self.container + if self.search(val): + return True + return False + # return val in self.container def search(self, val): """Return the node containing that value, else None.""" From d4e9f194dd87c68aed0e243d66285522a9a9ed40 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 16:35:18 -0800 Subject: [PATCH 27/87] fixed delete node with multiple children bug. --- src/bst.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/bst.py b/src/bst.py index daceae1..e8cac16 100644 --- a/src/bst.py +++ b/src/bst.py @@ -31,7 +31,7 @@ def __init__(self, if_iter=None): """Init of the Binary Search Tree class.""" self.root = None self.counter = 0 - self.container = [] + # self.container = [] if if_iter: try: for value in if_iter: @@ -48,7 +48,7 @@ def insert(self, val): if self.root is None: self.root = Node(val) self.counter += 1 - self.container.append(val) + # self.container.append(val) else: vertex = self.root @@ -59,7 +59,7 @@ def insert(self, val): else: vertex.right = Node(val) self.counter += 1 - self.container.append(val) + # self.container.append(val) break elif val < vertex.value: @@ -68,7 +68,7 @@ def insert(self, val): else: vertex.left = Node(val) self.counter += 1 - self.container.append(val) + # self.container.append(val) break else: break @@ -260,7 +260,9 @@ def delete(self, val): del_node = parent_of_del.right if min_parent is del_node: right = del_node.right - min_parent.right = right + del_node_left = del_node.left + parent_of_del.right = right + parent_of_del.right.left = del_node_left self.counter -= 1 return left = del_node.left @@ -281,7 +283,9 @@ def delete(self, val): del_node = parent_of_del.left if min_parent is del_node: left = del_node.right + del_node_left = del_node.left parent_of_del.left = left + parent_of_del.left.left = del_node_left self.counter -= 1 return left = del_node.left @@ -297,6 +301,7 @@ def delete(self, val): def _find_min_parent(self, vertex, side): """Find the parent of the replacement node, given the parent of the delete node.""" + import pdb; pdb.set_trace() if side == "right": if not vertex.right.right and not vertex.right.left: return From 9f0337e9540709c80584a48e3e5fbff7a09fbabd Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 19 Jan 2017 16:35:50 -0800 Subject: [PATCH 28/87] removed import pdb; pdb.set_trace() line in _find_min_parent. --- src/bst.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index e8cac16..363020f 100644 --- a/src/bst.py +++ b/src/bst.py @@ -301,7 +301,6 @@ def delete(self, val): def _find_min_parent(self, vertex, side): """Find the parent of the replacement node, given the parent of the delete node.""" - import pdb; pdb.set_trace() if side == "right": if not vertex.right.right and not vertex.right.left: return From 02c79de6e689cac99245ed94c8fa62ca460a6ea8 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Thu, 19 Jan 2017 16:37:42 -0800 Subject: [PATCH 29/87] more tests for delete. --- src/test_bst.py | 86 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/test_bst.py b/src/test_bst.py index 71d7d43..39a3a1a 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -3,6 +3,24 @@ import pytest BST_SIMPLE = [8, 10, 3, 14, 13, 1, 6, 7, 4] +BST_STRAIGHT_LINE = [1, 2, 3, 4, 5, 6, 7] +BST_BIG = [30, 50, 45, 60, 15, 25, 10, 47, 42, 40, 27, 55, 44, 46, 49, 7, 12, 65, 70, 57, 63, 52, 23, 4, 9, 11, 13, 21, 24, 26, 28] + + +@pytest.fixture +def big_bst(): + """Fixture to fill big bst.""" + from bst import BinarySearchTree + new_tree = BinarySearchTree(BST_BIG) + return new_tree + + +@pytest.fixture +def straight_bst(): + """Fixture to fill bst tree with a straight line of nodes down right side.""" + from bst import BinarySearchTree + new_tree = BinarySearchTree(BST_STRAIGHT_LINE) + return new_tree @pytest.fixture @@ -221,7 +239,7 @@ def test_delete_filled_root(filled_bst): assert a.in_order() == 14 -def test_delete_end_root(filled_bst): +def test_delete_end(filled_bst): """Test delete of root.""" a = filled_bst assert a.size() == 9 @@ -235,3 +253,69 @@ def test_delete_end_root(filled_bst): assert a.in_order() == 10 assert a.in_order() == 13 assert a.in_order() == 14 + +def test_delete_vertex_of_left_sub_head_in_bst(filled_bst): + """Test delete of a BST's left sub tree's head.""" + a = filled_bst + a.delete(3) + assert a.root.value == 8 + assert a.root.left.value == 4 + assert a.root.left.right.value == 6 + assert a.root.left.right.right.value == 7 + +def test_delete_vertex_of_lower_vertex_with_2_children(filled_bst): + """Test lower vertex removal at bottom of tree with 2 children.""" + a = filled_bst + a.delete(6) + assert a.root.value == 8 + assert a.root.left.right.value == 7 + assert a.root.left.right.left.value == 4 + assert a.root.left.value == 3 + assert a.root.left.left.value == 1 + assert a.root.right.value == 10 + +def test_delete(straight_bst): + """Test removal of node from straight line bst.""" + a = straight_bst + a.delete(4) + assert a.root.value == 1 + assert a.root.right.value == 2 + assert a.root.right.right.value == 3 + assert a.root.right.right.right.value == 5 + assert a.root.right.right.right.right.value == 6 + +def test_delete(straight_bst): + """Test removal of node from straight line bst.""" + a = straight_bst + a.delete(4) + assert a.root.value == 1 + assert a.root.right.value == 2 + assert a.root.right.right.value == 3 + assert a.root.right.right.right.value == 5 + assert a.root.right.right.right.right.value == 6 + + +def test_node_deletion_from_big_tree_with_grand_children(BST_BIG): + """Test deletion of node in big bst tree with children and grand children.""" + a = big_bst + a.delete(45) + assert a.root.right.left.value == 46 + assert a.root.right.left.right.value == 47 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.right.right.value == 49 + assert a.root.right.left.right.left.value is None + + +def test_deletion_from_big_tree_with_great_grand_children(BST_BIG): + """Test deletion of node in big bst tree with children and grand children.""" + a = big_bst + a.delete(50) + assert a.root.right.value == 52 + assert a.root.right.left.value == 45 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value is None + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 \ No newline at end of file From 50525935d87e95981236e37c736884d545974fc6 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Thu, 19 Jan 2017 16:42:08 -0800 Subject: [PATCH 30/87] added tests, merged new delete code. --- src/test_bst.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test_bst.py b/src/test_bst.py index 39a3a1a..09347b5 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -295,7 +295,7 @@ def test_delete(straight_bst): assert a.root.right.right.right.right.value == 6 -def test_node_deletion_from_big_tree_with_grand_children(BST_BIG): +def test_node_deletion_from_big_tree_with_grand_children(big_bst): """Test deletion of node in big bst tree with children and grand children.""" a = big_bst a.delete(45) @@ -306,7 +306,7 @@ def test_node_deletion_from_big_tree_with_grand_children(BST_BIG): assert a.root.right.left.right.left.value is None -def test_deletion_from_big_tree_with_great_grand_children(BST_BIG): +def test_deletion_from_big_tree_with_great_grand_children(big_bst): """Test deletion of node in big bst tree with children and grand children.""" a = big_bst a.delete(50) From 9abbf128c4248823fef76f42ce6a8f54a0d8a5bc Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sat, 21 Jan 2017 17:54:38 -0800 Subject: [PATCH 31/87] debug to past big_bst end delete tests. edited tests to check that new edge node is None. --- src/bst.py | 6 ++---- src/test_bst.py | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/bst.py b/src/bst.py index 363020f..7f8400a 100644 --- a/src/bst.py +++ b/src/bst.py @@ -268,8 +268,7 @@ def delete(self, val): left = del_node.left right = del_node.right min_node = min_parent.left - if min_node.right: - min_parent.left = min_node.right + min_parent.left = min_node.right del_node = min_node del_node.right = right del_node.left = left @@ -291,8 +290,7 @@ def delete(self, val): left = del_node.left right = del_node.right min_node = min_parent.left - if min_node.right: - min_parent.left = min_node.right + min_parent.left = min_node.right del_node = min_node del_node.right = right del_node.left = left diff --git a/src/test_bst.py b/src/test_bst.py index 09347b5..f47d93f 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -303,7 +303,7 @@ def test_node_deletion_from_big_tree_with_grand_children(big_bst): assert a.root.right.left.right.value == 47 assert a.root.right.left.left.value == 42 assert a.root.right.left.right.right.value == 49 - assert a.root.right.left.right.left.value is None + assert a.root.right.left.right.left is None def test_deletion_from_big_tree_with_great_grand_children(big_bst): @@ -316,6 +316,6 @@ def test_deletion_from_big_tree_with_great_grand_children(big_bst): assert a.root.right.right.left.value == 55 assert a.root.right.right.right.value == 65 assert a.root.right.right.left.right.value == 57 - assert a.root.right.right.left.left.value is None + assert a.root.right.right.left.left is None assert a.root.right.right.right.right.value == 70 assert a.root.right.right.right.left.value == 63 \ No newline at end of file From b5a49ac72fbbb3416ffd6d80111d6b2b92153897 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sat, 21 Jan 2017 18:33:41 -0800 Subject: [PATCH 32/87] edited test file, added tests. --- src/bst.py | 5 +- src/test_bst.py | 159 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 146 insertions(+), 18 deletions(-) diff --git a/src/bst.py b/src/bst.py index 7f8400a..5a56500 100644 --- a/src/bst.py +++ b/src/bst.py @@ -82,7 +82,6 @@ def contains(self, val): if self.search(val): return True return False - # return val in self.container def search(self, val): """Return the node containing that value, else None.""" @@ -224,11 +223,11 @@ def delete(self, val): while True: if not vertex.left.left: min_parent = vertex + break else: vertex = vertex.left min_node = min_parent.left - if min_node.right: - min_parent.left = min_node.right + min_parent.left = min_node.right self.root = min_node self.root.left = left self.root.right = right diff --git a/src/test_bst.py b/src/test_bst.py index f47d93f..ec99838 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -4,7 +4,11 @@ BST_SIMPLE = [8, 10, 3, 14, 13, 1, 6, 7, 4] BST_STRAIGHT_LINE = [1, 2, 3, 4, 5, 6, 7] -BST_BIG = [30, 50, 45, 60, 15, 25, 10, 47, 42, 40, 27, 55, 44, 46, 49, 7, 12, 65, 70, 57, 63, 52, 23, 4, 9, 11, 13, 21, 24, 26, 28] +BST_BIG = [ + 30, 50, 45, 60, 15, 25, 10, 47, 42, + 40, 27, 55, 44, 46, 49, 7, 12, 65, 70, 57, + 63, 52, 23, 4, 9, 11, 13, 21, 24, 26, 28 +] @pytest.fixture @@ -196,20 +200,20 @@ def test_balance(): def test_in_order_traversal_first_node_traversed_is_1(filled_bst): - """In-order traversal will start with 1. """ + """In-order traversal will start with 1.""" in_order_list = [] - for x in filled_bst.in_order(): + for x in filled_bst.in_order_trav(): in_order_list.append(x) assert in_order_list[0] == 1 -def test_in_order_traversal_first_node_traversed_is_1(filled_bst): +def test_in_order_traversal_first_node_traversed_is_1_reg(filled_bst): """In-order traversal's first value from generator will get a 1.""" assert filled_bst.in_order() == 1 def test_pre_order_traversal_first_node_traversed_is_1(filled_bst): - """Pre-order traversal will get """ + """Pre-order traversal will get 8.""" assert filled_bst.pre_order() == 8 @@ -254,6 +258,7 @@ def test_delete_end(filled_bst): assert a.in_order() == 13 assert a.in_order() == 14 + def test_delete_vertex_of_left_sub_head_in_bst(filled_bst): """Test delete of a BST's left sub tree's head.""" a = filled_bst @@ -263,6 +268,7 @@ def test_delete_vertex_of_left_sub_head_in_bst(filled_bst): assert a.root.left.right.value == 6 assert a.root.left.right.right.value == 7 + def test_delete_vertex_of_lower_vertex_with_2_children(filled_bst): """Test lower vertex removal at bottom of tree with 2 children.""" a = filled_bst @@ -274,15 +280,6 @@ def test_delete_vertex_of_lower_vertex_with_2_children(filled_bst): assert a.root.left.left.value == 1 assert a.root.right.value == 10 -def test_delete(straight_bst): - """Test removal of node from straight line bst.""" - a = straight_bst - a.delete(4) - assert a.root.value == 1 - assert a.root.right.value == 2 - assert a.root.right.right.value == 3 - assert a.root.right.right.right.value == 5 - assert a.root.right.right.right.right.value == 6 def test_delete(straight_bst): """Test removal of node from straight line bst.""" @@ -318,4 +315,136 @@ def test_deletion_from_big_tree_with_great_grand_children(big_bst): assert a.root.right.right.left.right.value == 57 assert a.root.right.right.left.left is None assert a.root.right.right.right.right.value == 70 - assert a.root.right.right.right.left.value == 63 \ No newline at end of file + assert a.root.right.right.right.left.value == 63 + + +def test_node_deletion_from_big_tree_root(big_bst): + """Test root deletion on big_bst.""" + a = big_bst + a.delete(30) + assert a.root.value == 40 + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left is None + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_node_deletion_from_big_tree_furthest_left(big_bst): + """Test furthest left node deletion on big_bst.""" + a = big_bst + a.delete(4) + assert a.root.value == 30 + assert a.root.left.value == 15 + assert a.root.left.left.value == 10 + assert a.root.left.left.left.value == 7 + assert a.root.left.left.left.left is None + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left.value == 40 + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_big_bst(big_bst): + """Test nodes in proper places in big_bst.""" + a = big_bst + assert a.root.value == 30 + assert a.root.left.value == 15 + assert a.root.left.right.value == 25 + assert a.root.left.right.left.value == 23 + assert a.root.left.left.value == 10 + assert a.root.left.left.right.value == 12 + assert a.root.left.left.right.right.value == 13 + assert a.root.left.left.right.left.value == 11 + assert a.root.left.left.left.value == 7 + assert a.root.left.left.left.right.value == 9 + assert a.root.left.left.left.left.value == 4 + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left.value == 40 + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_big_bst_insert_delete_min_node_with_right_child(big_bst): + """Test deletion of node with min node with right child.""" + a = big_bst + a.insert(22) + a.delete(15) + assert a.root.value == 30 + assert a.root.left.value == 21 + assert a.root.left.right.value == 25 + assert a.root.left.right.left.value == 23 + assert a.root.left.right.left.left.value == 22 + assert a.root.left.left.value == 10 + assert a.root.left.left.right.value == 12 + assert a.root.left.left.right.right.value == 13 + assert a.root.left.left.right.left.value == 11 + assert a.root.left.left.left.value == 7 + assert a.root.left.left.left.right.value == 9 + assert a.root.left.left.left.left.value == 4 + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left.value == 40 + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 + + +def test_big_bst_insert_delete_root_min_node_with_right_child(big_bst): + """Test deletion of root node with min node with right child.""" + a = big_bst + a.insert(41) + a.delete(30) + a = big_bst + assert a.root.value == 40 + assert a.root.left.value == 15 + assert a.root.left.right.value == 25 + assert a.root.left.right.left.value == 23 + assert a.root.left.left.value == 10 + assert a.root.left.left.right.value == 12 + assert a.root.left.left.right.right.value == 13 + assert a.root.left.left.right.left.value == 11 + assert a.root.left.left.left.value == 7 + assert a.root.left.left.left.right.value == 9 + assert a.root.left.left.left.left.value == 4 + assert a.root.right.value == 50 + assert a.root.right.left.value == 45 + assert a.root.right.left.left.value == 42 + assert a.root.right.left.left.left.value == 41 + assert a.root.right.left.left.right.value == 44 + assert a.root.right.right.value == 60 + assert a.root.right.right.left.value == 55 + assert a.root.right.right.right.value == 65 + assert a.root.right.right.left.right.value == 57 + assert a.root.right.right.left.left.value == 52 + assert a.root.right.right.right.right.value == 70 + assert a.root.right.right.right.left.value == 63 From 12ac5df8c1665f6e9d04e4bf93abdfa31b4f007e Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sat, 21 Jan 2017 18:54:17 -0800 Subject: [PATCH 33/87] added pre order and post order tests. --- src/test_bst.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/test_bst.py b/src/test_bst.py index ec99838..36825d5 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -210,15 +210,40 @@ def test_in_order_traversal_first_node_traversed_is_1(filled_bst): def test_in_order_traversal_first_node_traversed_is_1_reg(filled_bst): """In-order traversal's first value from generator will get a 1.""" assert filled_bst.in_order() == 1 + assert filled_bst.in_order() == 3 + assert filled_bst.in_order() == 4 + assert filled_bst.in_order() == 6 + assert filled_bst.in_order() == 7 + assert filled_bst.in_order() == 8 + assert filled_bst.in_order() == 10 + assert filled_bst.in_order() == 13 + assert filled_bst.in_order() == 14 -def test_pre_order_traversal_first_node_traversed_is_1(filled_bst): +def test_pre_order_traversal_first_node_traversed_is_8(filled_bst): """Pre-order traversal will get 8.""" assert filled_bst.pre_order() == 8 + assert filled_bst.pre_order() == 3 + assert filled_bst.pre_order() == 1 + assert filled_bst.pre_order() == 6 + assert filled_bst.pre_order() == 4 + assert filled_bst.pre_order() == 7 + assert filled_bst.pre_order() == 10 + assert filled_bst.pre_order() == 14 + assert filled_bst.pre_order() == 13 def test_post_order_traversal(filled_bst): """Post-order traversal.""" + assert filled_bst.post_order() == 1 + assert filled_bst.post_order() == 4 + assert filled_bst.post_order() == 7 + assert filled_bst.post_order() == 6 + assert filled_bst.post_order() == 3 + assert filled_bst.post_order() == 13 + assert filled_bst.post_order() == 14 + assert filled_bst.post_order() == 10 + assert filled_bst.post_order() == 8 def test_delete_empty(): From 6daedecd60b420c3462d7b671f571a329291ca50 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sun, 22 Jan 2017 11:08:17 -0800 Subject: [PATCH 34/87] commented out timeit section. --- src/bst.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bst.py b/src/bst.py index 5a56500..27b0de5 100644 --- a/src/bst.py +++ b/src/bst.py @@ -1,6 +1,8 @@ """Module for Binary Search Tree.""" from queue_ds import Queue +import timeit +import random class Node(object): @@ -323,3 +325,11 @@ def _find_min_parent(self, vertex, side): else: vertex = vertex.left return + + +# if __name__ == "__main__": + +# res1 = timeit.repeat(stmt="depth(g)", setup="from graph import g, depth", number=10, repeat=3) +# res2 = timeit.repeat(stmt="breadth(g)", setup="from graph import g, breadth", number=10, repeat=3) +# print("Depth First: ", res1) +# print("Breadth First: ", res2) From ab0ed103e1f044ba989fe0690d18a0e68d79cab6 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sun, 22 Jan 2017 13:53:00 -0800 Subject: [PATCH 35/87] new file, balanced bst. modified private functions to be marked as private. modified tests to use marked private functions where necessary. --- src/balanced_bst.py | 336 ++++++++++++++++++++++++++++++++++++++++++++ src/bst.py | 25 ++-- src/test_bst.py | 17 ++- 3 files changed, 365 insertions(+), 13 deletions(-) create mode 100644 src/balanced_bst.py diff --git a/src/balanced_bst.py b/src/balanced_bst.py new file mode 100644 index 0000000..2350457 --- /dev/null +++ b/src/balanced_bst.py @@ -0,0 +1,336 @@ +"""Module for Binary Search Tree.""" + +from queue_ds import Queue +import timeit +import random + + +class Node(object): + """Node class.""" + + def __init__(self, value=None, left=None, right=None): + """Init of the Node class.""" + self.value = value + self.left = left + self.right = right + + +class BinarySearchTree(object): + """Binary Search Tree.""" + + """insert(self, val): will insert the value val into the BST. If val is already present, it will be ignored.""" + """search(self, val): will return the node containing that value, else None""" + """size(self): will return the integer size of the BST (equal to the total number of values stored in the tree). It will return 0 if the tree is empty.""" + """depth(self): will return an integer representing the total number of levels in the tree. If there is one value, the depth should be 1, if two values it will be 2, if three values it may be 2 or three, depending, etc.""" + """contains(self, val): will return True if val is in the BST, False if not.""" + """balance(self): will return an integer, positive or negative that represents how well balanced the tree is. Trees which are higher on the left than the right should return a positive value, trees which are higher on the right than the left should return a negative value. An ideally-balanced tree should return 0.""" + """in_order(self): will return a generator that will return the values in the tree using in-order traversal, one at a time.""" + """pre_order(self): will return a generator that will return the values in the tree using pre-order traversal, one at a time.""" + """post_order(self): will return a generator that will return the values in the tree using post_order traversal, one at a time.""" + """breadth_first(self): will return a generator that will return the values in the tree using breadth-first traversal, one at a time.""" + + def __init__(self, if_iter=None): + """Init of the Binary Search Tree class.""" + self.root = None + self.counter = 0 + # self.container = [] + if if_iter: + try: + for value in if_iter: + self.insert(value) + except TypeError: + self.insert(if_iter) + self._in_order = self._in_order_trav() + self._pre_order = self._pre_order_trav() + self._post_order = self._post_order_trav() + self._breadth_first = self._breadth_first_trav() + + def insert(self, val): + """Take a value, inserts into Binary Search Tree at correct placement.""" + if self.root is None: + self.root = Node(val) + self.counter += 1 + # self.container.append(val) + + else: + vertex = self.root + while True: + if val > vertex.value: + if vertex.right: + vertex = vertex.right + else: + vertex.right = Node(val) + self.counter += 1 + # self.container.append(val) + break + + elif val < vertex.value: + if vertex.left: + vertex = vertex.left + else: + vertex.left = Node(val) + self.counter += 1 + # self.container.append(val) + break + else: + break + + def size(self): + """Return size of Binary Search Tree.""" + return self.counter + + def contains(self, val): + """Return True if val is in the BST, False if not.""" + if self.search(val): + return True + return False + + def search(self, val): + """Return the node containing that value, else None.""" + vertex = self.root + while vertex: + if val > vertex.value: + if not vertex.right: + return None + vertex = vertex.right + elif val < vertex.value: + if not vertex.left: + return None + vertex = vertex.left + else: + return vertex + return None + + def depth(self): + """ + Return an integer representing the total number of levels in the tree. + + If there is one value, the depth should be 1, if two values it will be 2, + if three values it may be 2 or three, depending, etc. + """ + return self._calc_depth(self.root) + + def _calc_depth(self, tree): + """Calculate the depth of the binary search tree recursively.""" + if tree is None: + return 0 + else: + return max(self._calc_depth(tree.right), self._calc_depth(tree.left)) + 1 + + def balance(self): + """ + Return an integer, positive or negative that represents how well balanced the tree is. + + Trees which are higher on the left than the right should return a positive value, + trees which are higher on the right than the left should return a negative value. + An ideally-balanced tree should return 0. + """ + if self.root is None: + return 0 + return self._calc_depth(self.root.right) - self._calc_depth(self.root.left) + + def in_order(self): + """Return.""" + return next(self._in_order) + + def _in_order_trav(self): + """Traverse in_order, yielding via generator.""" + vertex = self.root + visited = [] + while (visited or vertex is not None): + + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + yield vertex.value + vertex = vertex.right + + def pre_order(self): + """Return.""" + return next(self._pre_order) + + def _pre_order_trav(self): + """Traverse pre_order, yielding via generator.""" + vertex = self.root + visited = [] + while (visited or vertex is not None): + if vertex is not None: + yield vertex.value + visited.append(vertex) + vertex = vertex.left + else: + vertex = visited.pop() + vertex = vertex.right + + def post_order(self): + """Return.""" + return next(self._post_order) + + def _post_order_trav(self): + """Traverse pre_order, yielding via generator.""" + vertex = self.root + peek_vertex = None + last_vertex = None + visited = [] + while (visited or vertex is not None): + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + peek_vertex = visited[-1] + if peek_vertex.right and peek_vertex.right is not last_vertex: + vertex = peek_vertex.right + else: + yield peek_vertex.value + last_vertex = visited.pop() + + def breadth_first(self): + """Fill in later.""" + return next(self._breadth_first) + + def _breadth_first_trav(self): + """Traverse breadth first order, yielding a generator.""" + q = Queue() + q.enqueue(self.root) + while len(q) > 0: + vertex = q.dequeue() + yield vertex.value + if (vertex.left): + q.enqueue(vertex.left) + if (vertex.right): + q.enqueue(vertex.right) + + def delete(self, val): + """Remove val from the tree if present, if not present this method is a no-op. Return None in all cases.""" + vertex = self.root + parent_of_del = None + del_node = None + if self.root is None: + return + if self.root.value == val: + left = self.root.left + right = self.root.right + if not right: + self.root = self.root.left + self.counter -= 1 + return + if not right.left: + self.root = right + self.root.left = left + self.counter -= 1 + return + vertex = vertex.right + while True: + if not vertex.left.left: + min_parent = vertex + break + else: + vertex = vertex.left + min_node = min_parent.left + min_parent.left = min_node.right + self.root = min_node + self.root.left = left + self.root.right = right + self.counter -= 1 + else: + while True: + if vertex.right and val == vertex.right.value: + parent_of_del = vertex + del_node = parent_of_del.right + min_parent = self._find_min_parent(parent_of_del, "right") + break + elif vertex.left and val == vertex.left.value: + parent_of_del = vertex + del_node = parent_of_del.left + min_parent = self._find_min_parent(parent_of_del, "left") + break + elif val > vertex.value and vertex.right: + vertex = vertex.right + elif val < vertex.value and vertex.left: + vertex = vertex.left + else: + return + + if val == parent_of_del.right.value: + if not min_parent: + parent_of_del.right = None + self.counter -= 1 + return + del_node = parent_of_del.right + if min_parent is del_node: + right = del_node.right + del_node_left = del_node.left + parent_of_del.right = right + parent_of_del.right.left = del_node_left + self.counter -= 1 + return + left = del_node.left + right = del_node.right + min_node = min_parent.left + min_parent.left = min_node.right + del_node = min_node + del_node.right = right + del_node.left = left + parent_of_del.right = del_node + self.counter -= 1 + + elif val == parent_of_del.left.value: + if not min_parent: + parent_of_del.left = None + self.counter -= 1 + return + del_node = parent_of_del.left + if min_parent is del_node: + left = del_node.right + del_node_left = del_node.left + parent_of_del.left = left + parent_of_del.left.left = del_node_left + self.counter -= 1 + return + left = del_node.left + right = del_node.right + min_node = min_parent.left + min_parent.left = min_node.right + del_node = min_node + del_node.right = right + del_node.left = left + parent_of_del.left = del_node + self.counter -= 1 + + def _find_min_parent(self, vertex, side): + """Find the parent of the replacement node, given the parent of the delete node.""" + if side == "right": + if not vertex.right.right and not vertex.right.left: + return + if vertex.right.right and not vertex.right.right.left: + return vertex.right + elif vertex.right.right and vertex.right.right.left: + vertex = vertex.right.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left + else: + if not vertex.left.right and not vertex.left.left: + return + if vertex.left.right and not vertex.left.right.left: + return vertex.left + elif vertex.left.right and vertex.left.right.left: + vertex = vertex.left.right + while True: + if not vertex.left.left: + return vertex + else: + vertex = vertex.left + return + + +# if __name__ == "__main__": + +# res1 = timeit.repeat(stmt="depth(g)", setup="from graph import g, depth", number=10, repeat=3) +# res2 = timeit.repeat(stmt="breadth(g)", setup="from graph import g, breadth", number=10, repeat=3) +# print("Depth First: ", res1) +# print("Breadth First: ", res2) diff --git a/src/bst.py b/src/bst.py index 27b0de5..2350457 100644 --- a/src/bst.py +++ b/src/bst.py @@ -40,10 +40,10 @@ def __init__(self, if_iter=None): self.insert(value) except TypeError: self.insert(if_iter) - self._in_order = self.in_order_trav() - self._pre_order = self.pre_order_trav() - self._post_order = self.post_order_trav() - self._breadth_first = self.breadth_first_trav() + self._in_order = self._in_order_trav() + self._pre_order = self._pre_order_trav() + self._post_order = self._post_order_trav() + self._breadth_first = self._breadth_first_trav() def insert(self, val): """Take a value, inserts into Binary Search Tree at correct placement.""" @@ -108,14 +108,14 @@ def depth(self): If there is one value, the depth should be 1, if two values it will be 2, if three values it may be 2 or three, depending, etc. """ - return self.calc_depth(self.root) + return self._calc_depth(self.root) - def calc_depth(self, tree): + def _calc_depth(self, tree): """Calculate the depth of the binary search tree recursively.""" if tree is None: return 0 else: - return max(self.calc_depth(tree.right), self.calc_depth(tree.left)) + 1 + return max(self._calc_depth(tree.right), self._calc_depth(tree.left)) + 1 def balance(self): """ @@ -127,13 +127,13 @@ def balance(self): """ if self.root is None: return 0 - return self.calc_depth(self.root.right) - self.calc_depth(self.root.left) + return self._calc_depth(self.root.right) - self._calc_depth(self.root.left) def in_order(self): """Return.""" return next(self._in_order) - def in_order_trav(self): + def _in_order_trav(self): """Traverse in_order, yielding via generator.""" vertex = self.root visited = [] @@ -151,7 +151,7 @@ def pre_order(self): """Return.""" return next(self._pre_order) - def pre_order_trav(self): + def _pre_order_trav(self): """Traverse pre_order, yielding via generator.""" vertex = self.root visited = [] @@ -168,7 +168,7 @@ def post_order(self): """Return.""" return next(self._post_order) - def post_order_trav(self): + def _post_order_trav(self): """Traverse pre_order, yielding via generator.""" vertex = self.root peek_vertex = None @@ -190,7 +190,7 @@ def breadth_first(self): """Fill in later.""" return next(self._breadth_first) - def breadth_first_trav(self): + def _breadth_first_trav(self): """Traverse breadth first order, yielding a generator.""" q = Queue() q.enqueue(self.root) @@ -274,6 +274,7 @@ def delete(self, val): del_node.right = right del_node.left = left parent_of_del.right = del_node + self.counter -= 1 elif val == parent_of_del.left.value: if not min_parent: diff --git a/src/test_bst.py b/src/test_bst.py index 36825d5..9f4b4df 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -202,7 +202,7 @@ def test_balance(): def test_in_order_traversal_first_node_traversed_is_1(filled_bst): """In-order traversal will start with 1.""" in_order_list = [] - for x in filled_bst.in_order_trav(): + for x in filled_bst._in_order_trav(): in_order_list.append(x) assert in_order_list[0] == 1 @@ -473,3 +473,18 @@ def test_big_bst_insert_delete_root_min_node_with_right_child(big_bst): assert a.root.right.right.left.left.value == 52 assert a.root.right.right.right.right.value == 70 assert a.root.right.right.right.left.value == 63 + + +def test_multiple_delete_to_empty(filled_bst): + """Test that all nodes can be deleted and size output reflects this.""" + a = filled_bst + a.delete(8) + a.delete(10) + a.delete(3) + a.delete(14) + a.delete(13) + a.delete(1) + a.delete(6) + a.delete(7) + a.delete(4) + assert a.size() == 0 From ad1219b7c08441537e08c125ef93573649f64112 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sun, 22 Jan 2017 14:10:52 -0800 Subject: [PATCH 36/87] testing for balance function in some different cases. --- src/test_bst.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test_bst.py b/src/test_bst.py index 9f4b4df..6a39301 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -199,6 +199,20 @@ def test_balance(): assert a.balance() == 0 +def test_balance_unbalanced_right(filled_bst): + """Test the balance function.""" + filled_bst.insert(11) + filled_bst.insert(12) + assert filled_bst.balance() == 2 + + +def test_balance_unbalanced_left(filled_bst): + """Test the balance function.""" + filled_bst.insert(5) + filled_bst.insert(4.5) + assert filled_bst.balance() == -2 + + def test_in_order_traversal_first_node_traversed_is_1(filled_bst): """In-order traversal will start with 1.""" in_order_list = [] From e9a806b6fee8933f6e3262cd5cf80f5fe3c704b2 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sun, 22 Jan 2017 17:47:16 -0800 Subject: [PATCH 37/87] removed corpse code from bst.py. added parent and red properties to balanced bst. --- src/balanced_bst.py | 2 ++ src/bst.py | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 2350457..2b7c5c5 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -13,6 +13,8 @@ def __init__(self, value=None, left=None, right=None): self.value = value self.left = left self.right = right + self.parent = + self.red = False class BinarySearchTree(object): diff --git a/src/bst.py b/src/bst.py index 2350457..3db4b26 100644 --- a/src/bst.py +++ b/src/bst.py @@ -50,7 +50,6 @@ def insert(self, val): if self.root is None: self.root = Node(val) self.counter += 1 - # self.container.append(val) else: vertex = self.root @@ -61,7 +60,6 @@ def insert(self, val): else: vertex.right = Node(val) self.counter += 1 - # self.container.append(val) break elif val < vertex.value: @@ -70,7 +68,6 @@ def insert(self, val): else: vertex.left = Node(val) self.counter += 1 - # self.container.append(val) break else: break From db28edf3d82dad11e1471010f9de4067ddc87eba Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sun, 22 Jan 2017 17:50:26 -0800 Subject: [PATCH 38/87] edited docstrings. --- src/balanced_bst.py | 14 +++++--------- src/bst.py | 9 ++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 2b7c5c5..588b9ae 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -13,7 +13,7 @@ def __init__(self, value=None, left=None, right=None): self.value = value self.left = left self.right = right - self.parent = + self.parent = None self.red = False @@ -35,7 +35,6 @@ def __init__(self, if_iter=None): """Init of the Binary Search Tree class.""" self.root = None self.counter = 0 - # self.container = [] if if_iter: try: for value in if_iter: @@ -52,7 +51,6 @@ def insert(self, val): if self.root is None: self.root = Node(val) self.counter += 1 - # self.container.append(val) else: vertex = self.root @@ -63,7 +61,6 @@ def insert(self, val): else: vertex.right = Node(val) self.counter += 1 - # self.container.append(val) break elif val < vertex.value: @@ -72,7 +69,6 @@ def insert(self, val): else: vertex.left = Node(val) self.counter += 1 - # self.container.append(val) break else: break @@ -132,7 +128,7 @@ def balance(self): return self._calc_depth(self.root.right) - self._calc_depth(self.root.left) def in_order(self): - """Return.""" + """Return the next value from the generator _in_order.""" return next(self._in_order) def _in_order_trav(self): @@ -150,7 +146,7 @@ def _in_order_trav(self): vertex = vertex.right def pre_order(self): - """Return.""" + """Return the next value from the generator _pre_order.""" return next(self._pre_order) def _pre_order_trav(self): @@ -167,7 +163,7 @@ def _pre_order_trav(self): vertex = vertex.right def post_order(self): - """Return.""" + """Return the next value from the generator _post_order.""" return next(self._post_order) def _post_order_trav(self): @@ -189,7 +185,7 @@ def _post_order_trav(self): last_vertex = visited.pop() def breadth_first(self): - """Fill in later.""" + """Return the next value from the generator _breadth_first.""" return next(self._breadth_first) def _breadth_first_trav(self): diff --git a/src/bst.py b/src/bst.py index 3db4b26..dc8fbd2 100644 --- a/src/bst.py +++ b/src/bst.py @@ -33,7 +33,6 @@ def __init__(self, if_iter=None): """Init of the Binary Search Tree class.""" self.root = None self.counter = 0 - # self.container = [] if if_iter: try: for value in if_iter: @@ -127,7 +126,7 @@ def balance(self): return self._calc_depth(self.root.right) - self._calc_depth(self.root.left) def in_order(self): - """Return.""" + """Return the next value from the generator _in_order.""" return next(self._in_order) def _in_order_trav(self): @@ -145,7 +144,7 @@ def _in_order_trav(self): vertex = vertex.right def pre_order(self): - """Return.""" + """Return the next value from the generator _pre_order.""" return next(self._pre_order) def _pre_order_trav(self): @@ -162,7 +161,7 @@ def _pre_order_trav(self): vertex = vertex.right def post_order(self): - """Return.""" + """Return the next value from the generator _post_order.""" return next(self._post_order) def _post_order_trav(self): @@ -184,7 +183,7 @@ def _post_order_trav(self): last_vertex = visited.pop() def breadth_first(self): - """Fill in later.""" + """Return the next value from the generator _breadth_first.""" return next(self._breadth_first) def _breadth_first_trav(self): From 9afc1a514083513066ca5ecc957727b8b75823fd Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 15:03:24 -0800 Subject: [PATCH 39/87] removed redundant assignment in delete del_node = --- src/bst.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bst.py b/src/bst.py index dc8fbd2..79d3bbe 100644 --- a/src/bst.py +++ b/src/bst.py @@ -254,7 +254,6 @@ def delete(self, val): parent_of_del.right = None self.counter -= 1 return - del_node = parent_of_del.right if min_parent is del_node: right = del_node.right del_node_left = del_node.left @@ -277,7 +276,6 @@ def delete(self, val): parent_of_del.left = None self.counter -= 1 return - del_node = parent_of_del.left if min_parent is del_node: left = del_node.right del_node_left = del_node.left From a63a1d2772fcfcd6fc0463ef65249e28a2645b8a Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 15:21:06 -0800 Subject: [PATCH 40/87] fixtures for l, r, lr, rl, l/r/lr/rl with gc moves. --- src/test_balanced_bst.py | 90 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/test_balanced_bst.py diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py new file mode 100644 index 0000000..9c4ff6c --- /dev/null +++ b/src/test_balanced_bst.py @@ -0,0 +1,90 @@ +"""Test Module for Balanced Binary Search Tree.""" +from bst import BinarySearchTree +import pytest + +BST_ROT_2_R_1 = [3, 2] +BST_ROT_2_L_3 = [1, 2] +BST_ROT_4_R_1 = [5, 6, 4, 2] +BST_ROT_4_LR_2 = [5, 6, 3, 1] +BST_ROT_4_R_12 = [6, 8, 5, 10] +BST_ROT_4_RL_9 = [6, 8, 5, 10] +BST_ROT_5_L_GC_6 = [2, 1, 4, 3, 5] +BST_ROT_5_RL_GC_5 = [2, 1, 4, 3, 6] +# BST_ROT_5_R_GC_1 = [] +# BST_ROT_5_LR_GC_3 = [] + +# filled_bst_rot_2_r_1 +# filled_bst_rot_2_l_3 +# filled_bst_rot_4_r_1 +# filled_bst_rot_4_lr_2 +# filled_bst_rot_4_r_12 +# filled_bst_rot_4_rl_9 +# filled_bst_rot_5_l_gc_6 +# filled_bst_rot_5_rl_gc_5 +# filled_bst_rot_5_r_gc_1 +# filled_bst_rot_5_lr_gc_3 + +@pytest.fixture +def filled_bst_rot_2_r_1(): + """Fixture for a 2 node tree for a right rotation with insertion of 1.""" + new_tree = BinarySearchTree(BST_ROT_2_R_1) + return new_tree + +@pytest.fixture +def filled_bst_rot_2_l_3(): + """Fixture for a 2 node tree for a left rotation with insertion of 3.""" + new_tree = BinarySearchTree(BST_ROT_2_L_3) + return new_tree + +@pytest.fixture +def filled_bst_rot_4_r_1(): + """Fixture for a 4 node tree for a right rotation with insertion of 1.""" + new_tree = BinarySearchTree(BST_ROT_4_R_1) + return new_tree + +@pytest.fixture +def filled_bst_rot_4_lr_2(): + """Fixture for a 4 node tree for a left-right rotation with insertion of 2.""" + new_tree = BinarySearchTree(BST_ROT_4_LR_2) + return new_tree + +@pytest.fixture +def filled_bst_rot_4_r_12(): + """Fixture for a 4 node tree for a right rotation with insertion of 12.""" + new_tree = BinarySearchTree(BST_ROT_4_R_12) + return new_tree + +@pytest.fixture +def filled_bst_rot_4_rl_9(): + """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" + new_tree = BinarySearchTree(BST_ROT_4_RL_9) + return new_tree + +@pytest.fixture +def filled_bst_rot_5_l_gc_6(): + """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" + new_tree = BinarySearchTree(BST_ROT_5_L_GC_6) + return new_tree + +@pytest.fixture +def filled_bst_rot_5_rl_gc_5(): + """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" + new_tree = BinarySearchTree(BST_ROT_5_RL_GC_5) + return new_tree + +@pytest.fixture +def filled_bst_rot_5_r_gc_1(): + """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" + new_tree = BinarySearchTree(BST_ROT_5_R_GC_1) + return new_tree + +@pytest.fixture +def filled_bst_rot_5_lr_gc_3(): + """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" + new_tree = BinarySearchTree(BST_ROT_5_LR_GC_3) + return new_tree + +def test_simple_3_node_right_rotation(): + """Balance bst via right rotation, when adding 1 to tree.""" + filled_bst_rot_2_r_1 + assert() From 658f4dbc23848d8fa6f8e9e792ae1f8672daea37 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 15:26:00 -0800 Subject: [PATCH 41/87] added parent attribute to nodes. --- src/balanced_bst.py | 86 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 588b9ae..0db4966 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -14,7 +14,6 @@ def __init__(self, value=None, left=None, right=None): self.left = left self.right = right self.parent = None - self.red = False class BinarySearchTree(object): @@ -59,7 +58,9 @@ def insert(self, val): if vertex.right: vertex = vertex.right else: - vertex.right = Node(val) + new_node = Node(val) + vertex.right = new_node + new_node.parent = vertex self.counter += 1 break @@ -67,7 +68,9 @@ def insert(self, val): if vertex.left: vertex = vertex.left else: - vertex.left = Node(val) + new_node = Node(val) + vertex.left = new_node + new_node.parent = vertex self.counter += 1 break else: @@ -200,6 +203,28 @@ def _breadth_first_trav(self): if (vertex.right): q.enqueue(vertex.right) + def print_bst(self): + """Return.""" + thislevel = [self.root] + while thislevel: + nextlevel = [] + print_level = [] + counter = 0 + for n in thislevel: + print(print_level[counter]) + if n.left: + nextlevel.append(n.left) + print_level.append(n.left.value) + else: + print_level.append('_') + if n.right: + nextlevel.append(n.right) + print_level.append(n.right.value) + else: + print_level.append('_') + print() + thislevel = nextlevel + def delete(self, val): """Remove val from the tree if present, if not present this method is a no-op. Return None in all cases.""" vertex = self.root @@ -212,11 +237,13 @@ def delete(self, val): right = self.root.right if not right: self.root = self.root.left + self.root.parent = None self.counter -= 1 return if not right.left: self.root = right self.root.left = left + self.root.parent = None self.counter -= 1 return vertex = vertex.right @@ -228,9 +255,12 @@ def delete(self, val): vertex = vertex.left min_node = min_parent.left min_parent.left = min_node.right + if min_parent.left: + min_parent.left.parent = min_parent self.root = min_node self.root.left = left self.root.right = right + self.root.parent = None self.counter -= 1 else: while True: @@ -256,22 +286,33 @@ def delete(self, val): parent_of_del.right = None self.counter -= 1 return - del_node = parent_of_del.right if min_parent is del_node: right = del_node.right del_node_left = del_node.left parent_of_del.right = right + if right: + right.parent = parent_of_del parent_of_del.right.left = del_node_left + if del_node_left: + del_node_left.parent = parent_of_del.right self.counter -= 1 return left = del_node.left right = del_node.right min_node = min_parent.left min_parent.left = min_node.right + if min_parent.left: + min_parent.left.parent = min_parent del_node = min_node del_node.right = right + if right: + del_node.right.parent = del_node del_node.left = left + if left: + del_node.left.parent = del_node parent_of_del.right = del_node + if del_node: + parent_of_del.right.parent = parent_of_del self.counter -= 1 elif val == parent_of_del.left.value: @@ -279,22 +320,33 @@ def delete(self, val): parent_of_del.left = None self.counter -= 1 return - del_node = parent_of_del.left if min_parent is del_node: left = del_node.right del_node_left = del_node.left parent_of_del.left = left + if left: + left.parent = parent_of_del parent_of_del.left.left = del_node_left + if del_node_left: + del_node_left.parent = parent_of_del.right self.counter -= 1 return left = del_node.left right = del_node.right min_node = min_parent.left min_parent.left = min_node.right + if min_parent.left: + min_parent.left.parent = min_parent del_node = min_node del_node.right = right + if right: + del_node.right.parent = del_node del_node.left = left + if left: + del_node.left.parent = del_node parent_of_del.left = del_node + if del_node: + parent_of_del.left.parent = parent_of_del self.counter -= 1 def _find_min_parent(self, vertex, side): @@ -325,6 +377,30 @@ def _find_min_parent(self, vertex, side): vertex = vertex.left return + def _balance_tree(self, node): + self._left_rotation(node) + return node + + def _left_rotation(self, node): + self.root = node.right + self.root.left = node + self.root.left.parent = self.root + self.root.parent = None + return node + + def _left_left_case(): + pass + + def _right_right_case(): + pass + + def _left_right_case(): + pass + + def _right_left_case(): + pass + + # if __name__ == "__main__": From 6c3ad0170061d0e79c14bef0ce23755531866f00 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 15:26:26 -0800 Subject: [PATCH 42/87] small test changes. --- src/test_balanced_bst.py | 45 +++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index 9c4ff6c..edd99da 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -1,5 +1,5 @@ """Test Module for Balanced Binary Search Tree.""" -from bst import BinarySearchTree +from balanced_bst import BinarySearchTree import pytest BST_ROT_2_R_1 = [3, 2] @@ -24,67 +24,88 @@ # filled_bst_rot_5_r_gc_1 # filled_bst_rot_5_lr_gc_3 + @pytest.fixture def filled_bst_rot_2_r_1(): """Fixture for a 2 node tree for a right rotation with insertion of 1.""" new_tree = BinarySearchTree(BST_ROT_2_R_1) return new_tree + @pytest.fixture def filled_bst_rot_2_l_3(): """Fixture for a 2 node tree for a left rotation with insertion of 3.""" new_tree = BinarySearchTree(BST_ROT_2_L_3) return new_tree + @pytest.fixture def filled_bst_rot_4_r_1(): """Fixture for a 4 node tree for a right rotation with insertion of 1.""" new_tree = BinarySearchTree(BST_ROT_4_R_1) return new_tree + @pytest.fixture def filled_bst_rot_4_lr_2(): """Fixture for a 4 node tree for a left-right rotation with insertion of 2.""" new_tree = BinarySearchTree(BST_ROT_4_LR_2) return new_tree + @pytest.fixture def filled_bst_rot_4_r_12(): """Fixture for a 4 node tree for a right rotation with insertion of 12.""" new_tree = BinarySearchTree(BST_ROT_4_R_12) return new_tree + @pytest.fixture def filled_bst_rot_4_rl_9(): """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" new_tree = BinarySearchTree(BST_ROT_4_RL_9) return new_tree + @pytest.fixture def filled_bst_rot_5_l_gc_6(): """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" new_tree = BinarySearchTree(BST_ROT_5_L_GC_6) return new_tree + @pytest.fixture def filled_bst_rot_5_rl_gc_5(): """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" new_tree = BinarySearchTree(BST_ROT_5_RL_GC_5) return new_tree -@pytest.fixture -def filled_bst_rot_5_r_gc_1(): - """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" - new_tree = BinarySearchTree(BST_ROT_5_R_GC_1) - return new_tree -@pytest.fixture -def filled_bst_rot_5_lr_gc_3(): - """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" - new_tree = BinarySearchTree(BST_ROT_5_LR_GC_3) - return new_tree +# @pytest.fixture +# def filled_bst_rot_5_r_gc_1(): +# """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" +# new_tree = BinarySearchTree(BST_ROT_5_R_GC_1) +# return new_tree + + +# @pytest.fixture +# def filled_bst_rot_5_lr_gc_3(): +# """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" +# new_tree = BinarySearchTree(BST_ROT_5_LR_GC_3) +# return new_tree + def test_simple_3_node_right_rotation(): """Balance bst via right rotation, when adding 1 to tree.""" filled_bst_rot_2_r_1 - assert() + assert filled_bst_rot_2_l_3.root == 2 + assert filled_bst_rot_2_l_3.root.right == 3 + assert filled_bst_rot_2_l_3.root.left == 1 + + +def test_simple_3_node_right_rotation(): + """Balance bst via right rotation, when adding 1 to tree.""" + filled_bst_rot_2_l_3 + assert filled_bst_rot_2_l_3.root == 2 + assert filled_bst_rot_2_l_3.root.right == 3 + assert filled_bst_rot_2_l_3.root.left == 1 From b097c520d4f5fee9e9e440ab9dd45359130e6fcb Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 15:30:38 -0800 Subject: [PATCH 43/87] fixed initial tests. --- src/test_balanced_bst.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index edd99da..51bc72a 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -98,14 +98,16 @@ def filled_bst_rot_5_rl_gc_5(): def test_simple_3_node_right_rotation(): """Balance bst via right rotation, when adding 1 to tree.""" filled_bst_rot_2_r_1 - assert filled_bst_rot_2_l_3.root == 2 - assert filled_bst_rot_2_l_3.root.right == 3 - assert filled_bst_rot_2_l_3.root.left == 1 + a = filled_bst_rot_2_l_3.balance_tree() + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 -def test_simple_3_node_right_rotation(): +def test_simple_3_node_left_rotation(): """Balance bst via right rotation, when adding 1 to tree.""" filled_bst_rot_2_l_3 - assert filled_bst_rot_2_l_3.root == 2 - assert filled_bst_rot_2_l_3.root.right == 3 - assert filled_bst_rot_2_l_3.root.left == 1 + a = filled_bst_rot_2_l_3.balance_tree() + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 From 84d1df5b4023b3b2ff604f4039bd4517ea7f3283 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 15:35:28 -0800 Subject: [PATCH 44/87] fixing tests. --- src/test_balanced_bst.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index 51bc72a..3019010 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -97,8 +97,7 @@ def filled_bst_rot_5_rl_gc_5(): def test_simple_3_node_right_rotation(): """Balance bst via right rotation, when adding 1 to tree.""" - filled_bst_rot_2_r_1 - a = filled_bst_rot_2_l_3.balance_tree() + a = filled_bst_rot_2_r_1.insert(1) assert a.root.value == 2 assert a.root.right.value == 3 assert a.root.left.value == 1 @@ -106,8 +105,7 @@ def test_simple_3_node_right_rotation(): def test_simple_3_node_left_rotation(): """Balance bst via right rotation, when adding 1 to tree.""" - filled_bst_rot_2_l_3 - a = filled_bst_rot_2_l_3.balance_tree() + a = filled_bst_rot_2_l_3.insert(3) assert a.root.value == 2 assert a.root.right.value == 3 assert a.root.left.value == 1 From c31ac8be8640881789d5d0a88a96d67f64d5e685 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 15:43:03 -0800 Subject: [PATCH 45/87] setting up rebalance function to pass initial tests. --- src/balanced_bst.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 0db4966..c9f5f8e 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -75,6 +75,7 @@ def insert(self, val): break else: break + self._balance_tree(self.root) def size(self): """Return size of Binary Search Tree.""" @@ -348,6 +349,7 @@ def delete(self, val): if del_node: parent_of_del.left.parent = parent_of_del self.counter -= 1 + self._balance_tree() def _find_min_parent(self, vertex, side): """Find the parent of the replacement node, given the parent of the delete node.""" @@ -378,26 +380,28 @@ def _find_min_parent(self, vertex, side): return def _balance_tree(self, node): - self._left_rotation(node) - return node + if self.size() >= 3: + if self.root.right: + self._left_rotation(node) + else: + self._right_rotation(node) def _left_rotation(self, node): self.root = node.right self.root.left = node self.root.left.parent = self.root self.root.parent = None - return node - - def _left_left_case(): - pass - def _right_right_case(): - pass + def _right_rotation(self, node): + self.root = node.left + self.root.right = node + self.root.right.parent = self.root + self.root.parent = None - def _left_right_case(): + def _left_right_rotation(self, node): pass - def _right_left_case(): + def _right_left_rotation(self, node): pass From 6a9d41aeb223d62458409d019102e192ea3d13d6 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 15:47:00 -0800 Subject: [PATCH 46/87] adding more tests. --- src/test_balanced_bst.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index 3019010..3f206c3 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -6,10 +6,10 @@ BST_ROT_2_L_3 = [1, 2] BST_ROT_4_R_1 = [5, 6, 4, 2] BST_ROT_4_LR_2 = [5, 6, 3, 1] -BST_ROT_4_R_12 = [6, 8, 5, 10] +BST_ROT_4_L_12 = [6, 8, 5, 10] BST_ROT_4_RL_9 = [6, 8, 5, 10] BST_ROT_5_L_GC_6 = [2, 1, 4, 3, 5] -BST_ROT_5_RL_GC_5 = [2, 1, 4, 3, 6] +BST_ROT_5_L_GC_5 = [2, 1, 4, 3, 6] # BST_ROT_5_R_GC_1 = [] # BST_ROT_5_LR_GC_3 = [] @@ -54,9 +54,9 @@ def filled_bst_rot_4_lr_2(): @pytest.fixture -def filled_bst_rot_4_r_12(): - """Fixture for a 4 node tree for a right rotation with insertion of 12.""" - new_tree = BinarySearchTree(BST_ROT_4_R_12) +def filled_bst_rot_4_l_12(): + """Fixture for a 4 node tree for a left rotation with insertion of 12.""" + new_tree = BinarySearchTree(BST_ROT_4_L_12) return new_tree @@ -75,9 +75,9 @@ def filled_bst_rot_5_l_gc_6(): @pytest.fixture -def filled_bst_rot_5_rl_gc_5(): - """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" - new_tree = BinarySearchTree(BST_ROT_5_RL_GC_5) +def filled_bst_rot_5_l_gc_5(): + """Fixture for a 4 node tree for a left rotation with insertion of 9.""" + new_tree = BinarySearchTree(BST_ROT_5_L_GC_5) return new_tree @@ -95,17 +95,21 @@ def filled_bst_rot_5_rl_gc_5(): # return new_tree -def test_simple_3_node_right_rotation(): - """Balance bst via right rotation, when adding 1 to tree.""" - a = filled_bst_rot_2_r_1.insert(1) +def test_simple_3_node_right_rotation(filled_bst_rot_2_r_1): + """Balance bst via right rotation, when adding 1 to tree of 3, 2.""" + a = filled_bst_rot_2_r_1 + a.insert(1) assert a.root.value == 2 assert a.root.right.value == 3 assert a.root.left.value == 1 -def test_simple_3_node_left_rotation(): - """Balance bst via right rotation, when adding 1 to tree.""" - a = filled_bst_rot_2_l_3.insert(3) +def test_simple_3_node_left_rotation(filled_bst_rot_2_l_3): + """Balance bst via right rotation, when adding 3 to tree of 1, 2.""" + a = filled_bst_rot_2_l_3 + a.insert(3) assert a.root.value == 2 assert a.root.right.value == 3 assert a.root.left.value == 1 + +# def test_left_right \ No newline at end of file From a49b26a1a0e52e0630eb765360757310f5129f59 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 16:12:09 -0800 Subject: [PATCH 47/87] added balance check for balance tree, see if tree is unbalanced. --- src/balanced_bst.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index c9f5f8e..568ba63 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -380,21 +380,25 @@ def _find_min_parent(self, vertex, side): return def _balance_tree(self, node): - if self.size() >= 3: + if self.balance > 1 or self.balance < -1: if self.root.right: self._left_rotation(node) else: self._right_rotation(node) def _left_rotation(self, node): + node_right = node.right.left self.root = node.right self.root.left = node + self.root.left.right = node_right self.root.left.parent = self.root self.root.parent = None def _right_rotation(self, node): + node_left = node.left.right self.root = node.left self.root.right = node + self.root.right.left = node_left self.root.right.parent = self.root self.root.parent = None From a21719e5f90c1199a70ec0835c7253ae2f691690 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 16:51:18 -0800 Subject: [PATCH 48/87] added 1-14 sequential insert for balancing tests. --- src/test_balanced_bst.py | 362 ++++++++++++++++++++++++++++++++------- 1 file changed, 296 insertions(+), 66 deletions(-) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index 3f206c3..81e5002 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -4,25 +4,7 @@ BST_ROT_2_R_1 = [3, 2] BST_ROT_2_L_3 = [1, 2] -BST_ROT_4_R_1 = [5, 6, 4, 2] -BST_ROT_4_LR_2 = [5, 6, 3, 1] -BST_ROT_4_L_12 = [6, 8, 5, 10] -BST_ROT_4_RL_9 = [6, 8, 5, 10] -BST_ROT_5_L_GC_6 = [2, 1, 4, 3, 5] -BST_ROT_5_L_GC_5 = [2, 1, 4, 3, 6] -# BST_ROT_5_R_GC_1 = [] -# BST_ROT_5_LR_GC_3 = [] - -# filled_bst_rot_2_r_1 -# filled_bst_rot_2_l_3 -# filled_bst_rot_4_r_1 -# filled_bst_rot_4_lr_2 -# filled_bst_rot_4_r_12 -# filled_bst_rot_4_rl_9 -# filled_bst_rot_5_l_gc_6 -# filled_bst_rot_5_rl_gc_5 -# filled_bst_rot_5_r_gc_1 -# filled_bst_rot_5_lr_gc_3 +BST_ROT_LR_GC_7 = [10, 12, 5, 8, 2] @pytest.fixture @@ -40,76 +22,324 @@ def filled_bst_rot_2_l_3(): @pytest.fixture -def filled_bst_rot_4_r_1(): - """Fixture for a 4 node tree for a right rotation with insertion of 1.""" - new_tree = BinarySearchTree(BST_ROT_4_R_1) +def filled_bst_rot_lr_gc_7(): + """Fixture for a binary search tree for a left-right rotation with insertion of 7 to tree 10, 12, 5, 8, 2.""" + new_tree = BinarySearchTree(BST_ROT_LR_GC_7) return new_tree -@pytest.fixture -def filled_bst_rot_4_lr_2(): - """Fixture for a 4 node tree for a left-right rotation with insertion of 2.""" - new_tree = BinarySearchTree(BST_ROT_4_LR_2) - return new_tree - +def test_simple_3_node_right_rotation(filled_bst_rot_2_r_1): + """Balance bst via right rotation, when adding 1 to tree of 3, 2.""" + a = filled_bst_rot_2_r_1 + a.insert(1) + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 -@pytest.fixture -def filled_bst_rot_4_l_12(): - """Fixture for a 4 node tree for a left rotation with insertion of 12.""" - new_tree = BinarySearchTree(BST_ROT_4_L_12) - return new_tree +def test_simple_3_node_left_rotation(filled_bst_rot_2_l_3): + """Balance bst via left rotation, when adding 3 to tree of 1, 2.""" + a = filled_bst_rot_2_l_3 + a.insert(3) + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 -@pytest.fixture -def filled_bst_rot_4_rl_9(): - """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" - new_tree = BinarySearchTree(BST_ROT_4_RL_9) - return new_tree +def test_left_right_rotation(filled_bst_rot_lr_gc_7): + """Balance bst via left-right rotation, when adding 2 to tree of 5, 6, 3, 1.""" + a = filled_bst_rot_lr_gc_7 + a.insert(7) + assert a.root.value == 8 + assert a.root.right.value == 10 + assert a.root.left.value == 5 + assert a.root.right.left.value == 7 + assert a.root.left.left.value == 2 + assert a.root.right.right.value == 12 -@pytest.fixture -def filled_bst_rot_5_l_gc_6(): - """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" - new_tree = BinarySearchTree(BST_ROT_5_L_GC_6) - return new_tree +def test_bst_empty_tree(): + """Test balancing empty tree.""" + a = BinarySearchTree() + assert a.root.value is None -@pytest.fixture -def filled_bst_rot_5_l_gc_5(): - """Fixture for a 4 node tree for a left rotation with insertion of 9.""" - new_tree = BinarySearchTree(BST_ROT_5_L_GC_5) - return new_tree +def test_bst_1_value(): + """Test balancing tree with one value.""" + a = BinarySearchTree() + a.insert(1) + assert a.root.value == 1 + assert a.root.right.value is None + assert a.root.left.value is None -# @pytest.fixture -# def filled_bst_rot_5_r_gc_1(): -# """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" -# new_tree = BinarySearchTree(BST_ROT_5_R_GC_1) -# return new_tree +def test_bst_2_values(): + """Test balancing tree with two values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + assert a.root.value == 1 + assert a.root.right.value == 2 + assert a.root.left.value is None -# @pytest.fixture -# def filled_bst_rot_5_lr_gc_3(): -# """Fixture for a 4 node tree for a right-left rotation with insertion of 9.""" -# new_tree = BinarySearchTree(BST_ROT_5_LR_GC_3) -# return new_tree +def test_bst_3_values(): + """Test balancing tree with three values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + assert a.root.value == 2 + assert a.root.right.value == 3 + assert a.root.left.value == 1 -def test_simple_3_node_right_rotation(filled_bst_rot_2_r_1): - """Balance bst via right rotation, when adding 1 to tree of 3, 2.""" - a = filled_bst_rot_2_r_1 +def test_bst_4_values(): + """Test balancing tree with four values.""" + a = BinarySearchTree() a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) assert a.root.value == 2 assert a.root.right.value == 3 assert a.root.left.value == 1 + assert a.root.right.right.value == 4 -def test_simple_3_node_left_rotation(filled_bst_rot_2_l_3): - """Balance bst via right rotation, when adding 3 to tree of 1, 2.""" - a = filled_bst_rot_2_l_3 +def test_bst_5_values(): + """Test balancing tree with five values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) a.insert(3) + a.insert(4) + a.insert(5) assert a.root.value == 2 - assert a.root.right.value == 3 + assert a.root.right.value == 4 assert a.root.left.value == 1 + assert a.root.right.right.value == 5 + assert a.root.right.left.value == 3 + + +def test_bst_6_values(): + """Test balancing tree with six values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + assert a.root.value == 4 + assert a.root.right.value == 5 + assert a.root.left.value == 2 + assert a.root.right.right.value == 6 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + + +def test_bst_7_values(): + """Test balancing tree with seven values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + assert a.root.value == 4 + assert a.root.right.value == 6 + assert a.root.left.value == 2 + assert a.root.right.right.value == 7 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.left.value == 5 + + +def test_bst_8_values(): + """Test balancing tree with eight values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + assert a.root.value == 4 + assert a.root.right.value == 6 + assert a.root.left.value == 2 + assert a.root.right.right.value == 7 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.left.value == 5 + assert a.root.right.right.right.value == 8 + +def test_bst_9_values(): + """Test balancing tree with nine values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + assert a.root.value == 4 + assert a.root.right.value == 6 + assert a.root.left.value == 2 + assert a.root.right.right.value == 8 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.left.value == 5 + assert a.root.right.right.right.value == 9 + assert a.root.right.right.left.value == 9 -# def test_left_right \ No newline at end of file + +def test_bst_10_values(): + """Test balancing tree with ten values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + assert a.root.value == 4 + assert a.root.right.value == 8 + assert a.root.left.value == 2 + assert a.root.right.right.value == 9 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.right.right.value == 10 + assert a.root.right.left.value == 6 + assert a.root.right.left.right.value == 7 + assert a.root.right.left.left.value == 5 + + +def test_bst_11_values(): + """Test balancing tree with eleven values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + a.insert(11) + assert a.root.value == 4 + assert a.root.right.value == 8 + assert a.root.left.value == 2 + assert a.root.right.right.value == 10 + assert a.root.left.left.value == 1 + assert a.root.left.right.value == 3 + assert a.root.right.right.right.value == 11 + assert a.root.right.right.left.value == 9 + assert a.root.right.left.value == 6 + assert a.root.right.left.right.value == 7 + assert a.root.right.left.left.value == 5 + + +def test_bst_12_values(): + """Test balancing tree with twelve values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + a.insert(11) + a.insert(12) + assert a.root.value == 8 + assert a.root.right.value == 10 + assert a.root.right.right.value == 11 + assert a.root.right.left.value == 9 + assert a.root.right.right.right.value == 12 + assert a.root.left.value == 4 + assert a.root.left.left.value == 2 + assert a.root.left.left.left.value == 1 + assert a.root.left.left.right.value == 3 + assert a.root.left.right.value == 6 + assert a.root.left.right.left.value == 5 + assert a.root.left.right.right.value == 7 + + +def test_bst_13_values(): + """Test balancing tree with thirteen values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + a.insert(11) + a.insert(12) + a.insert(13) + assert a.root.value == 8 + assert a.root.right.value == 10 + assert a.root.right.right.value == 12 + assert a.root.right.left.value == 9 + assert a.root.right.right.right.value == 13 + assert a.root.right.right.left.value == 11 + assert a.root.left.value == 4 + assert a.root.left.left.value == 2 + assert a.root.left.left.left.value == 1 + assert a.root.left.left.right.value == 3 + assert a.root.left.right.value == 6 + assert a.root.left.right.left.value == 5 + assert a.root.left.right.right.value == 7 + + +def test_bst_14_values(): + """Test balancing tree with fourteen values.""" + a = BinarySearchTree() + a.insert(1) + a.insert(2) + a.insert(3) + a.insert(4) + a.insert(5) + a.insert(6) + a.insert(7) + a.insert(8) + a.insert(9) + a.insert(10) + a.insert(11) + a.insert(12) + a.insert(13) + a.insert(14) + assert a.root.value == 8 + assert a.root.right.value == 12 + assert a.root.right.right.value == 13 + assert a.root.right.left.value == 10 + assert a.root.right.right.right.value == 14 + assert a.root.right.left.left.value == 9 + assert a.root.right.left.right.value == 11 + assert a.root.left.value == 4 + assert a.root.left.left.value == 2 + assert a.root.left.left.left.value == 1 + assert a.root.left.left.right.value == 3 + assert a.root.left.right.value == 6 + assert a.root.left.right.left.value == 5 + assert a.root.left.right.right.value == 7 From 9bc5031593df9da5a57b60e5a1d724c94cbb401c Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 17:30:35 -0800 Subject: [PATCH 49/87] tests from 14 down to 1 added for balance bst. --- src/test_balanced_bst.py | 286 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 285 insertions(+), 1 deletion(-) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index 81e5002..1ea5e6d 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -57,6 +57,7 @@ def test_left_right_rotation(filled_bst_rot_lr_gc_7): assert a.root.left.left.value == 2 assert a.root.right.right.value == 12 + def test_bst_empty_tree(): """Test balancing empty tree.""" a = BinarySearchTree() @@ -177,6 +178,7 @@ def test_bst_8_values(): assert a.root.right.left.value == 5 assert a.root.right.right.right.value == 8 + def test_bst_9_values(): """Test balancing tree with nine values.""" a = BinarySearchTree() @@ -280,7 +282,7 @@ def test_bst_12_values(): assert a.root.left.right.left.value == 5 assert a.root.left.right.right.value == 7 - + def test_bst_13_values(): """Test balancing tree with thirteen values.""" a = BinarySearchTree() @@ -343,3 +345,285 @@ def test_bst_14_values(): assert a.root.left.right.value == 6 assert a.root.left.right.left.value == 5 assert a.root.left.right.right.value == 7 + + +def test_bst_1_value_starting_at_14_down(): + """Test balancing tree with one value.""" + a = BinarySearchTree() + a.insert(14) + assert a.root.value == 14 + assert a.root.right.value is None + assert a.root.left.value is None + + +def test_bst_2_values_starting_at_14_down(): + """Test balancing tree with two values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + assert a.root.value == 14 + assert a.root.right.value is None + assert a.root.left.value == 13 + + +def test_bst_3_values_starting_at_14_down(): + """Test balancing tree with three values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + assert a.root.value == 13 + assert a.root.right.value == 14 + assert a.root.left.value == 12 + + +def test_bst_4_values_starting_at_14_down(): + """Test balancing tree with four values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + assert a.root.value == 13 + assert a.root.right.value == 14 + assert a.root.left.value == 12 + assert a.root.left.left.value == 11 + + +def test_bst_5_values_starting_at_14_down(): + """Test balancing tree with five values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + assert a.root.value == 13 + assert a.root.right.value == 14 + assert a.root.left.value == 11 + assert a.root.left.right.value == 12 + assert a.root.left.left.value == 10 + + +def test_bst_6_values_starting_at_14_down(): + """Test balancing tree with six values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.left.value == 10 + assert a.root.right.right.value == 14 + assert a.root.right.left.value == 12 + assert a.root.left.left.value == 9 + + +def test_bst_7_values_starting_at_14_down(): + """Test balancing tree with seven values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.left.value == 9 + assert a.root.right.right.value == 14 + assert a.root.left.left.value == 8 + assert a.root.left.right.value == 10 + assert a.root.right.left.value == 12 + + +def test_bst_8_values_starting_at_14_down(): + """Test balancing tree with eight values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.left.value == 9 + assert a.root.right.right.value == 14 + assert a.root.left.left.value == 8 + assert a.root.left.right.value == 10 + assert a.root.right.left.value == 12 + assert a.root.left.left.left.value == 7 + + +def test_bst_9_values_starting_at_14_down(): + """Test balancing tree with nine values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.left.value == 9 + assert a.root.right.right.value == 14 + assert a.root.left.left.value == 7 + assert a.root.left.right.value == 10 + assert a.root.right.left.value == 12 + assert a.root.left.left.left.value == 6 + assert a.root.left.left.right.value == 8 + + +def test_bst_10_values_starting_at_14_down(): + """Test balancing tree with ten values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.right.right.value == 14 + assert a.root.right.left.value == 12 + assert a.root.left.value == 7 + assert a.root.left.left.value == 6 + assert a.root.left.right.value == 9 + assert a.root.left.left.left.value == 5 + assert a.root.left.right.right.value == 10 + assert a.root.right.right.left.value == 8 + + +def test_bst_11_values_starting_at_14_down(): + """Test balancing tree with eleven values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + a.insert(4) + assert a.root.value == 11 + assert a.root.right.value == 13 + assert a.root.right.right.value == 14 + assert a.root.right.left.value == 12 + assert a.root.left.value == 7 + assert a.root.left.left.value == 5 + assert a.root.left.right.value == 9 + assert a.root.left.left.left.value == 4 + assert a.root.left.right.right.value == 10 + assert a.root.left.right.left.value == 8 + assert a.root.left.left.right.value == 6 + + +def test_bst_12_values_starting_at_14_down(): + """Test balancing tree with twelve values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + a.insert(4) + a.insert(3) + assert a.root.value == 7 + assert a.root.right.value == 11 + assert a.root.right.right.value == 13 + assert a.root.right.left.value == 9 + assert a.root.right.left.left.value == 8 + assert a.root.right.left.right.value == 10 + assert a.root.right.right.left.value == 12 + assert a.root.right.right.right.value == 14 + assert a.root.left.value == 5 + assert a.root.left.left.value == 4 + assert a.root.left.right.value == 6 + assert a.root.left.left.left.value == 3 + + +def test_bst_13_values_starting_at_14_down(): + """Test balancing tree with thirteen values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + a.insert(4) + a.insert(3) + a.insert(2) + assert a.root.value == 7 + assert a.root.right.value == 11 + assert a.root.right.right.value == 13 + assert a.root.right.left.value == 9 + assert a.root.right.left.left.value == 8 + assert a.root.right.left.right.value == 10 + assert a.root.right.right.left.value == 12 + assert a.root.right.right.right.value == 14 + assert a.root.left.value == 5 + assert a.root.left.left.value == 3 + assert a.root.left.right.value == 6 + assert a.root.left.left.left.value == 2 + assert a.root.left.left.right.value == 4 + + +def test_bst_14_values_starting_at_14_down(): + """Test balancing tree with fourteen values.""" + a = BinarySearchTree() + a.insert(14) + a.insert(13) + a.insert(12) + a.insert(11) + a.insert(10) + a.insert(9) + a.insert(8) + a.insert(7) + a.insert(6) + a.insert(5) + a.insert(4) + a.insert(3) + a.insert(2) + assert a.root.value == 7 + assert a.root.right.value == 11 + assert a.root.right.right.value == 13 + assert a.root.right.left.value == 9 + assert a.root.right.left.left.value == 8 + assert a.root.right.left.right.value == 10 + assert a.root.right.right.left.value == 12 + assert a.root.right.right.right.value == 14 + assert a.root.left.value == 3 + assert a.root.left.left.value == 2 + assert a.root.left.right.value == 5 + assert a.root.left.left.left.value == 1 + assert a.root.left.right.right.value == 6 + assert a.root.left.right.left.value == 4 From be53c2950fec5f38ce95e0c4cebc02920cab2bff Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 17:49:42 -0800 Subject: [PATCH 50/87] implementation of left and right rotations. --- src/balanced_bst.py | 201 +++++++++++++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 69 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 568ba63..6e8c723 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -45,38 +45,6 @@ def __init__(self, if_iter=None): self._post_order = self._post_order_trav() self._breadth_first = self._breadth_first_trav() - def insert(self, val): - """Take a value, inserts into Binary Search Tree at correct placement.""" - if self.root is None: - self.root = Node(val) - self.counter += 1 - - else: - vertex = self.root - while True: - if val > vertex.value: - if vertex.right: - vertex = vertex.right - else: - new_node = Node(val) - vertex.right = new_node - new_node.parent = vertex - self.counter += 1 - break - - elif val < vertex.value: - if vertex.left: - vertex = vertex.left - else: - new_node = Node(val) - vertex.left = new_node - new_node.parent = vertex - self.counter += 1 - break - else: - break - self._balance_tree(self.root) - def size(self): """Return size of Binary Search Tree.""" return self.counter @@ -129,7 +97,11 @@ def balance(self): """ if self.root is None: return 0 - return self._calc_depth(self.root.right) - self._calc_depth(self.root.left) + return self._calc_balance(self.root) + + def _calc_balance(self, node): + """Calculate the balance of a subtree at node.""" + return self._calc_depth(node.right) - self._calc_depth(node.left) def in_order(self): """Return the next value from the generator _in_order.""" @@ -204,27 +176,37 @@ def _breadth_first_trav(self): if (vertex.right): q.enqueue(vertex.right) - def print_bst(self): - """Return.""" - thislevel = [self.root] - while thislevel: - nextlevel = [] - print_level = [] - counter = 0 - for n in thislevel: - print(print_level[counter]) - if n.left: - nextlevel.append(n.left) - print_level.append(n.left.value) - else: - print_level.append('_') - if n.right: - nextlevel.append(n.right) - print_level.append(n.right.value) + def insert(self, val): + """Take a value, inserts into Binary Search Tree at correct placement.""" + if self.root is None: + self.root = Node(val) + self.counter += 1 + + else: + vertex = self.root + while True: + if val > vertex.value: + if vertex.right: + vertex = vertex.right + else: + new_node = Node(val) + vertex.right = new_node + new_node.parent = vertex + self.counter += 1 + break + + elif val < vertex.value: + if vertex.left: + vertex = vertex.left + else: + new_node = Node(val) + vertex.left = new_node + new_node.parent = vertex + self.counter += 1 + break else: - print_level.append('_') - print() - thislevel = nextlevel + break + self._balance_tree() def delete(self, val): """Remove val from the tree if present, if not present this method is a no-op. Return None in all cases.""" @@ -379,28 +361,57 @@ def _find_min_parent(self, vertex, side): vertex = vertex.left return - def _balance_tree(self, node): - if self.balance > 1 or self.balance < -1: - if self.root.right: + def _balance_tree(self): + # import pdb;pdb.set_trace() + for node in self._post_order_node(): + if self._calc_balance(node) > 1: self._left_rotation(node) - else: + elif self._calc_balance(node) < -1: self._right_rotation(node) def _left_rotation(self, node): - node_right = node.right.left - self.root = node.right - self.root.left = node - self.root.left.right = node_right - self.root.left.parent = self.root - self.root.parent = None + a = node + a_parent = a.parent + b = a.right + d = b.left + if a is self.root: + self.root = b + self.root.parent = None + self.root.left = a + a.parent = self.root + a.right = d + if d: + d.parent = a + return + a_parent.right = b + b.parent = a_parent + b.left = a + a.parent = b + a.right = d + if d: + d.parent = a def _right_rotation(self, node): - node_left = node.left.right - self.root = node.left - self.root.right = node - self.root.right.left = node_left - self.root.right.parent = self.root - self.root.parent = None + a = node + a_parent = a.parent + b = a.left + d = b.right + if a is self.root: + self.root = b + self.root.parent = None + self.root.right = a + a.parent = self.root + a.left = d + if d: + d.parent = a + return + a_parent.left = b + b.parent = a_parent + b.right = a + a.parent = b + a.left = d + if d: + d.parent = a def _left_right_rotation(self, node): pass @@ -408,6 +419,58 @@ def _left_right_rotation(self, node): def _right_left_rotation(self, node): pass + def _post_order_node(self): + vertex = self.root + peek_vertex = None + last_vertex = None + visited = [] + while (visited or vertex is not None): + if vertex is not None: + visited.append(vertex) + vertex = vertex.left + else: + peek_vertex = visited[-1] + if peek_vertex.right and peek_vertex.right is not last_vertex: + vertex = peek_vertex.right + else: + yield peek_vertex + last_vertex = visited.pop() + + # def balance(self): + # """ + # Return an integer, positive or negative that represents how well balanced the tree is. + + # Trees which are higher on the left than the right should return a positive value, + # trees which are higher on the right than the left should return a negative value. + # An ideally-balanced tree should return 0. + # """ + # if self.root is None: + # return 0 + # return self._calc_depth(self.root.right) - self._calc_depth(self.root.left) + + + # def print_bst(self): + # """Return.""" + # thislevel = [self.root] + # while thislevel: + # nextlevel = [] + # print_level = [] + # counter = 0 + # for n in thislevel: + # print(print_level[counter]) + # if n.left: + # nextlevel.append(n.left) + # print_level.append(n.left.value) + # else: + # print_level.append('_') + # if n.right: + # nextlevel.append(n.right) + # print_level.append(n.right.value) + # else: + # print_level.append('_') + # print() + # thislevel = nextlevel + # if __name__ == "__main__": From 85bd424d82a32c361d5209356c9ca973b1b9f33c Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 18:16:15 -0800 Subject: [PATCH 51/87] beginning implementation of _left_right and _right_left rotations. --- src/balanced_bst.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 6e8c723..5eb53eb 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -414,10 +414,12 @@ def _right_rotation(self, node): d.parent = a def _left_right_rotation(self, node): - pass + + self._right_rotation(node) def _right_left_rotation(self, node): - pass + + self._left_rotation(node) def _post_order_node(self): vertex = self.root From 9e2b4d1870bb08e4a8c0c1c6bb7fda374906bea8 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 18:16:30 -0800 Subject: [PATCH 52/87] fixed all but one test. --- src/test_balanced_bst.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index 1ea5e6d..5f02d7b 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -61,7 +61,7 @@ def test_left_right_rotation(filled_bst_rot_lr_gc_7): def test_bst_empty_tree(): """Test balancing empty tree.""" a = BinarySearchTree() - assert a.root.value is None + assert a.root is None def test_bst_1_value(): @@ -69,8 +69,8 @@ def test_bst_1_value(): a = BinarySearchTree() a.insert(1) assert a.root.value == 1 - assert a.root.right.value is None - assert a.root.left.value is None + assert a.root.right is None + assert a.root.left is None def test_bst_2_values(): @@ -80,7 +80,7 @@ def test_bst_2_values(): a.insert(2) assert a.root.value == 1 assert a.root.right.value == 2 - assert a.root.left.value is None + assert a.root.left is None def test_bst_3_values(): @@ -199,7 +199,7 @@ def test_bst_9_values(): assert a.root.left.right.value == 3 assert a.root.right.left.value == 5 assert a.root.right.right.right.value == 9 - assert a.root.right.right.left.value == 9 + assert a.root.right.right.left.value == 7 def test_bst_10_values(): @@ -352,8 +352,8 @@ def test_bst_1_value_starting_at_14_down(): a = BinarySearchTree() a.insert(14) assert a.root.value == 14 - assert a.root.right.value is None - assert a.root.left.value is None + assert a.root.right is None + assert a.root.left is None def test_bst_2_values_starting_at_14_down(): @@ -362,7 +362,7 @@ def test_bst_2_values_starting_at_14_down(): a.insert(14) a.insert(13) assert a.root.value == 14 - assert a.root.right.value is None + assert a.root.right is None assert a.root.left.value == 13 @@ -507,7 +507,7 @@ def test_bst_10_values_starting_at_14_down(): assert a.root.left.right.value == 9 assert a.root.left.left.left.value == 5 assert a.root.left.right.right.value == 10 - assert a.root.right.right.left.value == 8 + assert a.root.left.right.left.value == 8 def test_bst_11_values_starting_at_14_down(): @@ -613,6 +613,7 @@ def test_bst_14_values_starting_at_14_down(): a.insert(4) a.insert(3) a.insert(2) + a.insert(1) assert a.root.value == 7 assert a.root.right.value == 11 assert a.root.right.right.value == 13 From 609345690aeeb75aa6f121aed68d93999f804b76 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 19:57:24 -0800 Subject: [PATCH 53/87] tests for left_right rotations. --- src/test_balanced_bst.py | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index 5f02d7b..ffd9d47 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -628,3 +628,51 @@ def test_bst_14_values_starting_at_14_down(): assert a.root.left.left.left.value == 1 assert a.root.left.right.right.value == 6 assert a.root.left.right.left.value == 4 + + +def test_bst_left_right_basic(filled_bst_rot_2_r_1): + """Test left right rotation.""" + a = filled_bst_rot_2_r_1 + a.insert(2.5) + assert a.root.value == 2.5 + assert a.root.parent is None + assert a.root.left.value == 2 + assert a.root.right.value == 3 + + +def test_bst_right_left_basic(filled_bst_rot_2_l_3): + """Test right left rotation.""" + a = filled_bst_rot_2_l_3 + a.insert(1.5) + assert a.root.value == 1.5 + assert a.root.parent is None + assert a.root.left.value == 1 + assert a.root.right.value == 2 + + +def test_bst_left_right_root_left_right_add_left(filled_bst_rot_lr_gc_7): + """Test left right rotation.""" + a = filled_bst_rot_lr_gc_7 + a.insert(7) + assert a.root.value == 8 + assert a.root.parent is None + assert a.root.left.value == 5 + assert a.root.left.left.value == 2 + assert a.root.left.right.value == 7 + assert a.root.right.value == 10 + assert a.root.right.left.value is None + assert a.root.right.right.value == 12 + + +def test_bst_left_right_root_left_right_add_right(filled_bst_rot_lr_gc_7): + """Test left right rotation.""" + a = filled_bst_rot_lr_gc_7 + a.insert(9) + assert a.root.value == 8 + assert a.root.parent is None + assert a.root.left.value == 5 + assert a.root.left.left.value == 2 + assert a.root.left.right is None + assert a.root.right.value == 10 + assert a.root.right.left.value == 9 + assert a.root.right.right.value == 12 From 1bd5746e9bd7dc2440df9e8d4c5684e90017e31f Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 20:06:45 -0800 Subject: [PATCH 54/87] tests for right left rotations. --- src/test_balanced_bst.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index ffd9d47..d6c1906 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -5,6 +5,7 @@ BST_ROT_2_R_1 = [3, 2] BST_ROT_2_L_3 = [1, 2] BST_ROT_LR_GC_7 = [10, 12, 5, 8, 2] +BST_ROT_RL = [10, 5, 15, 13, 20] @pytest.fixture @@ -27,6 +28,12 @@ def filled_bst_rot_lr_gc_7(): new_tree = BinarySearchTree(BST_ROT_LR_GC_7) return new_tree +@pytest.fixture +def filled_bst_rot_rl(): + """Fixture for a binary search tree for a right-left rotation.""" + new_tree = BinarySearchTree(BST_ROT_RL) + return new_tree + def test_simple_3_node_right_rotation(filled_bst_rot_2_r_1): """Balance bst via right rotation, when adding 1 to tree of 3, 2.""" @@ -676,3 +683,31 @@ def test_bst_left_right_root_left_right_add_right(filled_bst_rot_lr_gc_7): assert a.root.right.value == 10 assert a.root.right.left.value == 9 assert a.root.right.right.value == 12 + + +def test_right_left_root_right_left_add_left(filled_bst_rot_rl): + """Test right left rotation.""" + a = filled_bst_rot_rl + a.insert(12) + assert a.root.value == 13 + assert a.root.parent is None + assert a.root.left.value == 10 + assert a.root.left.left.value == 5 + assert a.root.left.right.value == 12 + assert a.root.right.value == 15 + assert a.root.right.left is None + assert a.root.right.right.value == 20 + + +def test_right_left_root_right_left_add_right(filled_bst_rot_rl): + """Test right left rotation.""" + a = filled_bst_rot_rl + a.insert(12) + assert a.root.value == 13 + assert a.root.parent is None + assert a.root.left.value == 10 + assert a.root.left.left.value == 5 + assert a.root.left.right is None + assert a.root.right.value == 15 + assert a.root.right.left.value == 14 + assert a.root.right.right.value == 20 From 1005e73fce457be3f09c64747c09acdd995d6ee9 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 20:28:17 -0800 Subject: [PATCH 55/87] left-right and right-left various functions. --- src/balanced_bst.py | 68 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 5eb53eb..0b263eb 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -362,12 +362,17 @@ def _find_min_parent(self, vertex, side): return def _balance_tree(self): - # import pdb;pdb.set_trace() for node in self._post_order_node(): if self._calc_balance(node) > 1: - self._left_rotation(node) + if self._calc_balance(node.right.left) > self._calc_balance(node.right.right): + self._right_left_rotation(node) + else: + self._left_rotation(node) elif self._calc_balance(node) < -1: - self._right_rotation(node) + if self._calc_balance(node.left.right) > self._calc_balance(node.left.left): + self._left_right_rotation(node) + else: + self._right_rotation(node) def _left_rotation(self, node): a = node @@ -414,12 +419,61 @@ def _right_rotation(self, node): d.parent = a def _left_right_rotation(self, node): - - self._right_rotation(node) + """Try left right rotation on node.""" + if node.left.right.left.value < node.left.right.value: + vertex = node + left_head = node.left + right_sub = node.left.right + switcher = node.left.right.left + switcher.parent = left_sub + left_head.parent = right_sub + left_head.right = switcher + right_sub.parent = vertex.parent + right_sub.right = vertex + right_sub.left = left_head + vertex.parent = right_sub + vertex.left = None + else: + vertex = node + left_head = node.left + right_sub = node.left.right + switcher = node.left.right.right + switcher.parent = vertex + vertex.left = switcher + right_sub.parent = vertex.parent + vertex.parent = right_sub + right_sub.right = vertex + right_sub.left = left_head + left_head.parent = right_sub + left_head.right = None def _right_left_rotation(self, node): - - self._left_rotation(node) + """Try right left rotation on node.""" + if node.right.left.left.value > node.right.left.value: + vertex = node + right_head = node.right + left_sub = node.right.left + switcher = node.right.left.right + switcher.parent = right_head + right_head.parent = left_sub + right_head.left = switcher + left_sub.parent = vertex.parent + left_sub.right = right_head + left_sub.left = vertex + vertex.parent = left_sub + vertex.right = None + else: + vertex = node + right_head = node.right + left_sub = node.right.left + switcher = node.right.left.left + switcher.parent = vertex + left_sub.parent = vertex.parent + vertex.right = switcher + vertex.parent = left_sub + left_sub.left = vertex + left_sub.right = right_head + right_head.left = None def _post_order_node(self): vertex = self.root From 0be96a039d7d89614dbc0e9164c208c9330951ce Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 23 Jan 2017 21:10:32 -0800 Subject: [PATCH 56/87] trying to fix. --- src/balanced_bst.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 0b263eb..37a798b 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -364,12 +364,12 @@ def _find_min_parent(self, vertex, side): def _balance_tree(self): for node in self._post_order_node(): if self._calc_balance(node) > 1: - if self._calc_balance(node.right.left) > self._calc_balance(node.right.right): + if self._calc_balance(node.right) < 0: self._right_left_rotation(node) else: self._left_rotation(node) elif self._calc_balance(node) < -1: - if self._calc_balance(node.left.right) > self._calc_balance(node.left.left): + if self._calc_balance(node.left) > 0: self._left_right_rotation(node) else: self._right_rotation(node) @@ -420,15 +420,16 @@ def _right_rotation(self, node): def _left_right_rotation(self, node): """Try left right rotation on node.""" - if node.left.right.left.value < node.left.right.value: + if node.left.right.left and node.left.right and node.left.right.left.value < node.left.right.value: vertex = node left_head = node.left right_sub = node.left.right switcher = node.left.right.left - switcher.parent = left_sub + switcher.parent = left_head left_head.parent = right_sub left_head.right = switcher right_sub.parent = vertex.parent + vertex.parent. right_sub.right = vertex right_sub.left = left_head vertex.parent = right_sub @@ -449,7 +450,7 @@ def _left_right_rotation(self, node): def _right_left_rotation(self, node): """Try right left rotation on node.""" - if node.right.left.left.value > node.right.left.value: + if node.right.left.left and node.right.left and node.right.left.left.value > node.right.left.value: vertex = node right_head = node.right left_sub = node.right.left From 9c430138330f0bc25d0b8d54dd6290a4df22b441 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 21:41:56 -0800 Subject: [PATCH 57/87] added file for hash_table.py --- src/hash_table.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/hash_table.py diff --git a/src/hash_table.py b/src/hash_table.py new file mode 100644 index 0000000..3ae4f3c --- /dev/null +++ b/src/hash_table.py @@ -0,0 +1,25 @@ +# HASH TABLE (HT) +# +# CodeFellows 401d5 +# Submission Date: +# +# Authors: Colin Lamont +# Ben Shields +# +# URL: + + +""" +The Mailroom module allows the user to +track donations and format emails for donors. +The user can write tailored thank you emails +given donor names and donation amounts. +The user can create a report that is +a list of donor names and their corresponding donation +histories, arranged by in order of the total amount +donated. +The user can add a donor name to this list. +The user can quit the program from Main Menu, and +may return to Main Menu at any time. +""" + From 329d538d859247d79d25d7d7a7ba9e7e5d330813 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Mon, 23 Jan 2017 22:50:40 -0800 Subject: [PATCH 58/87] initial implementation of hashtable. created test file. --- src/hash_table.py | 38 +++++++++++++++++++++++++++++++++++++- src/test_hash_table.py | 4 ++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/test_hash_table.py diff --git a/src/hash_table.py b/src/hash_table.py index 3ae4f3c..613e16b 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -1,5 +1,6 @@ + # HASH TABLE (HT) -# +# # CodeFellows 401d5 # Submission Date: # @@ -23,3 +24,38 @@ may return to Main Menu at any time. """ + +class HashTable(object): + """Hash Table.""" + + """get(key) - should return the value stored with the given key""" + """set(key, val) - should store the given val using the given key""" + """_hash(key) - should hash the key provided (note that this is an internal api)""" + + def __init__(self): + """Init function for the Hash Table class.""" + self._container = [] + self._num_buckets = 1000 + for i in range(0, self._num_buckets): + self._container.append([]) + + def get(self, key): + """Return the value corresponding to this key in the hash table.""" + hashed_value = self._hash(key) + for each in self._container[hashed_value]: + if each[0] == key: + return each[1] + return 'Key not in hash table.' + + def set(self, key, val): + """Place the key value pair in the hash table.""" + hashed_value = self._hash(key) + (self._container[hashed_value]).append((key, val)) + return + + def _hash(self, key): + """Docstring.""" + num = 0 + for each in key: + num += ord(each) + return num % self._num_buckets diff --git a/src/test_hash_table.py b/src/test_hash_table.py new file mode 100644 index 0000000..60e1e16 --- /dev/null +++ b/src/test_hash_table.py @@ -0,0 +1,4 @@ +"""Test module for Hash Table.""" + +from hash_table import HashTable +import pytest From 8cbd2b077ebd32a01d1fe500ba447545c4097717 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Tue, 24 Jan 2017 00:34:08 -0800 Subject: [PATCH 59/87] fixed left-right and right-left. --- src/balanced_bst.py | 62 ++++++++++++++++++++++++++++++---------- src/test_balanced_bst.py | 18 ++++++------ 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 37a798b..9904c9a 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -420,16 +420,23 @@ def _right_rotation(self, node): def _left_right_rotation(self, node): """Try left right rotation on node.""" - if node.left.right.left and node.left.right and node.left.right.left.value < node.left.right.value: + if node.left.right.left: vertex = node left_head = node.left right_sub = node.left.right switcher = node.left.right.left + if vertex.parent: + right_sub.parent = vertex.parent + if vertex.parent.value > vertex.value: + vertex.parent.left = right_sub + else: + vertex.parent.right = right_sub + else: + self.root = right_sub + right_sub.parent = None switcher.parent = left_head left_head.parent = right_sub left_head.right = switcher - right_sub.parent = vertex.parent - vertex.parent. right_sub.right = vertex right_sub.left = left_head vertex.parent = right_sub @@ -439,9 +446,17 @@ def _left_right_rotation(self, node): left_head = node.left right_sub = node.left.right switcher = node.left.right.right + if vertex.parent: + right_sub.parent = vertex.parent + if vertex.parent.value > vertex.value: + vertex.parent.left = right_sub + else: + vertex.parent.right = right_sub + else: + self.root = right_sub + right_sub.parent = None switcher.parent = vertex vertex.left = switcher - right_sub.parent = vertex.parent vertex.parent = right_sub right_sub.right = vertex right_sub.left = left_head @@ -450,31 +465,48 @@ def _left_right_rotation(self, node): def _right_left_rotation(self, node): """Try right left rotation on node.""" - if node.right.left.left and node.right.left and node.right.left.left.value > node.right.left.value: + if node.right.left.left: vertex = node right_head = node.right left_sub = node.right.left - switcher = node.right.left.right + switcher = node.right.left.left + if vertex.parent: + left_sub.parent = vertex.parent + if vertex.parent.value > vertex.value: + vertex.parent.left = left_sub + else: + vertex.parent.right = left_sub + else: + self.root = left_sub + left_sub.parent = None switcher.parent = right_head - right_head.parent = left_sub - right_head.left = switcher - left_sub.parent = vertex.parent + right_head.parent = vertex + right_head.left = None left_sub.right = right_head left_sub.left = vertex vertex.parent = left_sub - vertex.right = None + vertex.right = switcher else: vertex = node right_head = node.right left_sub = node.right.left - switcher = node.right.left.left - switcher.parent = vertex - left_sub.parent = vertex.parent - vertex.right = switcher + switcher = node.right.left.right + if vertex.parent: + left_sub.parent = vertex.parent + if vertex.parent.value > vertex.value: + vertex.parent.left = left_sub + else: + vertex.parent.right = left_sub + else: + self.root = left_sub + left_sub.parent = None + switcher.parent = right_head + vertex.right = None vertex.parent = left_sub left_sub.left = vertex left_sub.right = right_head - right_head.left = None + right_head.left = switcher + right_head.parent = left_sub def _post_order_node(self): vertex = self.root diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index ffd9d47..dcf9042 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -47,13 +47,13 @@ def test_simple_3_node_left_rotation(filled_bst_rot_2_l_3): def test_left_right_rotation(filled_bst_rot_lr_gc_7): - """Balance bst via left-right rotation, when adding 2 to tree of 5, 6, 3, 1.""" + """Balance bst via left-right rotation, when adding 7 to tree of 10, 12, 5, 8, 2.""" a = filled_bst_rot_lr_gc_7 a.insert(7) assert a.root.value == 8 assert a.root.right.value == 10 assert a.root.left.value == 5 - assert a.root.right.left.value == 7 + assert a.root.left.right.value == 7 assert a.root.left.left.value == 2 assert a.root.right.right.value == 12 @@ -633,21 +633,21 @@ def test_bst_14_values_starting_at_14_down(): def test_bst_left_right_basic(filled_bst_rot_2_r_1): """Test left right rotation.""" a = filled_bst_rot_2_r_1 - a.insert(2.5) - assert a.root.value == 2.5 + a.insert(1) + assert a.root.value == 2 assert a.root.parent is None - assert a.root.left.value == 2 + assert a.root.left.value == 1 assert a.root.right.value == 3 def test_bst_right_left_basic(filled_bst_rot_2_l_3): """Test right left rotation.""" a = filled_bst_rot_2_l_3 - a.insert(1.5) - assert a.root.value == 1.5 + a.insert(3) + assert a.root.value == 2 assert a.root.parent is None assert a.root.left.value == 1 - assert a.root.right.value == 2 + assert a.root.right.value == 3 def test_bst_left_right_root_left_right_add_left(filled_bst_rot_lr_gc_7): @@ -660,7 +660,7 @@ def test_bst_left_right_root_left_right_add_left(filled_bst_rot_lr_gc_7): assert a.root.left.left.value == 2 assert a.root.left.right.value == 7 assert a.root.right.value == 10 - assert a.root.right.left.value is None + assert a.root.right.left is None assert a.root.right.right.value == 12 From 4227245dce912bb9f6b98ef5e0da10fd0a951b38 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Tue, 24 Jan 2017 09:01:11 -0800 Subject: [PATCH 60/87] added more double rotation tests. --- src/test_balanced_bst.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index dcf9042..0b579cf 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -676,3 +676,27 @@ def test_bst_left_right_root_left_right_add_right(filled_bst_rot_lr_gc_7): assert a.root.right.value == 10 assert a.root.right.left.value == 9 assert a.root.right.right.value == 12 + + +def test_bst_double_rotation_one(): + """Test double rotation one.""" + a = BinarySearchTree([10, 5, 15, 13, 20]) + a.insert(12) + assert a.root.value == 13 + assert a.root.right.value == 15 + assert a.root.left.value == 10 + assert a.root.right.right.value == 20 + assert a.root.left.left.value == 5 + assert a.root.left.right.value == 12 + + +def test_bst_double_rotation_two(): + """Test double rotation one.""" + a = BinarySearchTree([15, 10, 20, 5, 13,]) + a.insert(14) + assert a.root.value == 13 + assert a.root.right.value == 15 + assert a.root.left.value == 10 + assert a.root.right.right.value == 20 + assert a.root.right.left.value == 14 + assert a.root.left.left.value == 5 From 3bdc95a7250cb566afd0fd5b6defffaf11c8057c Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Tue, 24 Jan 2017 11:09:28 -0800 Subject: [PATCH 61/87] edited description. --- src/hash_table.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/hash_table.py b/src/hash_table.py index 613e16b..3185bac 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -11,23 +11,21 @@ """ -The Mailroom module allows the user to -track donations and format emails for donors. -The user can write tailored thank you emails -given donor names and donation amounts. -The user can create a report that is -a list of donor names and their corresponding donation -histories, arranged by in order of the total amount -donated. -The user can add a donor name to this list. -The user can quit the program from Main Menu, and -may return to Main Menu at any time. +The HashTable is a data structure that implements an +assosiative array. The structure maps keys to values +in such a way that values may be accessed in O(1) time. +This is done through use of a hashing function. +The hashing function maps the content of the key to +a specific bucket. As the content of the key always +maps to a specific hash value, the key's value may +be accessed quickly. """ class HashTable(object): """Hash Table.""" + """self._num_buckets - the number of buckets in the Hash Table.""" """get(key) - should return the value stored with the given key""" """set(key, val) - should store the given val using the given key""" """_hash(key) - should hash the key provided (note that this is an internal api)""" From df2b94597f8197365e907a49d71f327a8489f542 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Wed, 25 Jan 2017 13:22:55 -0800 Subject: [PATCH 62/87] list comprehensions, check for existing key. --- src/hash_table.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/hash_table.py b/src/hash_table.py index 3185bac..4629bb9 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -26,16 +26,14 @@ class HashTable(object): """Hash Table.""" """self._num_buckets - the number of buckets in the Hash Table.""" - """get(key) - should return the value stored with the given key""" - """set(key, val) - should store the given val using the given key""" - """_hash(key) - should hash the key provided (note that this is an internal api)""" + """get(key) - should return the value stored with the given key.""" + """set(key, val) - store the given val using the given key.""" + """_hash(key) - hash the key provided (note that this is an internal api).""" - def __init__(self): + def __init__(self, type='additive'): """Init function for the Hash Table class.""" - self._container = [] self._num_buckets = 1000 - for i in range(0, self._num_buckets): - self._container.append([]) + self._container = [[] for i in range(0, self._num_buckets)] def get(self, key): """Return the value corresponding to this key in the hash table.""" @@ -48,12 +46,11 @@ def get(self, key): def set(self, key, val): """Place the key value pair in the hash table.""" hashed_value = self._hash(key) + for each in self._container[hashed_value]: + if each[0] == key: + (self._container[hashed_value]).remove(each) (self._container[hashed_value]).append((key, val)) - return def _hash(self, key): - """Docstring.""" - num = 0 - for each in key: - num += ord(each) - return num % self._num_buckets + """Additive hash function.""" + return sum([ord(each) for each in key]) % self._num_buckets From 728cd6eae8b696c5fa8f8efdc6745965f4f4d238 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Wed, 25 Jan 2017 15:14:38 -0800 Subject: [PATCH 63/87] halfway thru refactoring to work for 3 nodes on delete. --- src/balanced_bst.py | 51 ++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 9904c9a..312b63d 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -222,12 +222,14 @@ def delete(self, val): self.root = self.root.left self.root.parent = None self.counter -= 1 + self._balance_tree() return if not right.left: self.root = right self.root.left = left self.root.parent = None self.counter -= 1 + self._balance_tree() return vertex = vertex.right while True: @@ -245,6 +247,7 @@ def delete(self, val): self.root.right = right self.root.parent = None self.counter -= 1 + self._balance_tree() else: while True: if vertex.right and val == vertex.right.value: @@ -262,12 +265,14 @@ def delete(self, val): elif val < vertex.value and vertex.left: vertex = vertex.left else: + self._balance_tree() return if val == parent_of_del.right.value: if not min_parent: parent_of_del.right = None self.counter -= 1 + self._balance_tree() return if min_parent is del_node: right = del_node.right @@ -279,6 +284,7 @@ def delete(self, val): if del_node_left: del_node_left.parent = parent_of_del.right self.counter -= 1 + self._balance_tree() return left = del_node.left right = del_node.right @@ -302,6 +308,7 @@ def delete(self, val): if not min_parent: parent_of_del.left = None self.counter -= 1 + self._balance_tree() return if min_parent is del_node: left = del_node.right @@ -313,6 +320,7 @@ def delete(self, val): if del_node_left: del_node_left.parent = parent_of_del.right self.counter -= 1 + self._balance_tree() return left = del_node.left right = del_node.right @@ -420,11 +428,12 @@ def _right_rotation(self, node): def _left_right_rotation(self, node): """Try left right rotation on node.""" - if node.left.right.left: + if self._calc_balance(node.left) > 0: vertex = node left_head = node.left right_sub = node.left.right - switcher = node.left.right.left + if node.left.right.left: + switcher = node.left.right.left if vertex.parent: right_sub.parent = vertex.parent if vertex.parent.value > vertex.value: @@ -434,9 +443,11 @@ def _left_right_rotation(self, node): else: self.root = right_sub right_sub.parent = None - switcher.parent = left_head + if node.left.right.left: + switcher.parent = left_head left_head.parent = right_sub - left_head.right = switcher + if node.left.right.left: + left_head.right = switcher right_sub.right = vertex right_sub.left = left_head vertex.parent = right_sub @@ -445,7 +456,8 @@ def _left_right_rotation(self, node): vertex = node left_head = node.left right_sub = node.left.right - switcher = node.left.right.right + if node.left.right.right: + switcher = node.left.right.right if vertex.parent: right_sub.parent = vertex.parent if vertex.parent.value > vertex.value: @@ -455,8 +467,10 @@ def _left_right_rotation(self, node): else: self.root = right_sub right_sub.parent = None - switcher.parent = vertex - vertex.left = switcher + if node.left.right.right: + switcher.parent = vertex + if node.left.right.right: + vertex.left = switcher vertex.parent = right_sub right_sub.right = vertex right_sub.left = left_head @@ -465,11 +479,12 @@ def _left_right_rotation(self, node): def _right_left_rotation(self, node): """Try right left rotation on node.""" - if node.right.left.left: + if self._calc_balance(node.right) < 0: vertex = node right_head = node.right left_sub = node.right.left - switcher = node.right.left.left + if node.right.left.left: + switcher = node.right.left.left if vertex.parent: left_sub.parent = vertex.parent if vertex.parent.value > vertex.value: @@ -479,18 +494,24 @@ def _right_left_rotation(self, node): else: self.root = left_sub left_sub.parent = None - switcher.parent = right_head + if node.right.left.left: + switcher.parent = right_head right_head.parent = vertex right_head.left = None left_sub.right = right_head left_sub.left = vertex vertex.parent = left_sub - vertex.right = switcher + if node.right.left.left: + vertex.right = switcher + else: + vertex.right = None else: + import pdb; pdb.set_trace() vertex = node right_head = node.right left_sub = node.right.left - switcher = node.right.left.right + if node.right.left.right: + switcher = node.right.left.right if vertex.parent: left_sub.parent = vertex.parent if vertex.parent.value > vertex.value: @@ -500,12 +521,14 @@ def _right_left_rotation(self, node): else: self.root = left_sub left_sub.parent = None - switcher.parent = right_head + if node.right.left.right: + switcher.parent = right_head vertex.right = None vertex.parent = left_sub left_sub.left = vertex left_sub.right = right_head - right_head.left = switcher + if node.right.left.right: + right_head.left = switcher right_head.parent = left_sub def _post_order_node(self): From 1ce911afe198ee3f99a064c82d2a11b0c796add1 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Wed, 25 Jan 2017 16:01:40 -0800 Subject: [PATCH 64/87] added travis yml file for data structures. --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1fc8989 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: python +python: + - "2.7" + - "3.5" + + +# command to install dependencies +install: + - pip install -e .[test] + # - pip install coveralls + +# command to run tests +script: + - py.test --cov=. --cover-report term-missing From d437c4f94f167ac1d9a32e05ca1a1dcd3d213a02 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Wed, 25 Jan 2017 16:03:08 -0800 Subject: [PATCH 65/87] tests for hashtable using words file. added colin ben hash. cleaned up code. --- src/hash_table.py | 123 ++++++++++++++++++++++++++++++++++++++- src/test_balanced_bst.py | 1 + src/test_hash_table.py | 52 +++++++++++++++++ 3 files changed, 174 insertions(+), 2 deletions(-) diff --git a/src/hash_table.py b/src/hash_table.py index 4629bb9..03b24f6 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -30,10 +30,11 @@ class HashTable(object): """set(key, val) - store the given val using the given key.""" """_hash(key) - hash the key provided (note that this is an internal api).""" - def __init__(self, type='additive'): + def __init__(self, hash_type='additive'): """Init function for the Hash Table class.""" - self._num_buckets = 1000 + self._num_buckets = 50000 self._container = [[] for i in range(0, self._num_buckets)] + self._type = hash_type def get(self, key): """Return the value corresponding to this key in the hash table.""" @@ -45,6 +46,8 @@ def get(self, key): def set(self, key, val): """Place the key value pair in the hash table.""" + if type(key) is not str: + return 'Keys must be strings.' hashed_value = self._hash(key) for each in self._container[hashed_value]: if each[0] == key: @@ -53,4 +56,120 @@ def set(self, key, val): def _hash(self, key): """Additive hash function.""" + if self._type == 'additive': + return self._additive_hash(key) + if self._type == 'colin-ben': + return self._colin_ben_hash(key) + + def _additive_hash(self, key): return sum([ord(each) for each in key]) % self._num_buckets + + def _colin_ben_hash(self, key): + ords = [] + sieve = gen_primes() + for each in key: + num = ord(each) + if ord(each) % 2 == 0: + num = int(str(ord(each))[::-1]) + ords.append([num, next(sieve)]) + a_sum = sum([each[0] * each[1] for each in ords]) + return a_sum % self._num_buckets + + # def _colin_ben_hash(self, key): + # ords = [] + # sieve = gen_primes() + # for each in key: + # num = ord(each) + # if ord(each) % 2 == 0: + # num = int(str(ord(each))[::-1]) + # ords.append([num, next(sieve)]) + # a_sum = sum([(each[0] << next(sieve)) * (each[1] << next(sieve)) for each in ords]) + # return a_sum % self._num_buckets + +# def genPrimes(): +# def isPrime(n): +# if n % 2 == 0: return n == 2 +# d = 3 +# while d * d <= n: +# if n % d == 0: return False +# d += 2 +# return True +# def init(): # change to Sieve of Eratosthenes +# ps, qs, sieve = [], [], [True] * 50000 +# p, m = 3, 0 +# while p * p <= 100000: +# if isPrime(p): +# ps.insert(0, p) +# qs.insert(0, p + (p-1) / 2) +# m += 1 +# p += 2 +# for i in xrange(m): +# for j in xrange(qs[i], 50000, ps[i]): +# sieve[j] = False +# return m, ps, qs, sieve +# def advance(m, ps, qs, sieve, bottom): +# for i in xrange(50000): sieve[i] = True +# for i in xrange(m): +# qs[i] = (qs[i] - 50000) % ps[i] +# p = ps[0] + 2 +# while p * p <= bottom + 100000: +# if isPrime(p): +# ps.insert(0, p) +# qs.insert(0, (p*p - bottom - 1)/2) +# m += 1 +# p += 2 +# for i in xrange(m): +# for j in xrange(qs[i], 50000, ps[i]): +# sieve[j] = False +# return m, ps, qs, sieve +# m, ps, qs, sieve = init() +# bottom, i = 0, 1 +# yield 2 +# while True: +# if i == 50000: +# bottom = bottom + 100000 +# m, ps, qs, sieve = advance(m, ps, qs, sieve, bottom) +# i = 0 +# elif sieve[i]: +# yield bottom + i + i + 1 +# i += 1 +# else: i += 1 + +# Sieve of Eratosthenes +# Code by David Eppstein, UC Irvine, 28 Feb 2002 +# http://code.activestate.com/recipes/117119/ + + +def gen_primes(): + """ Generate an infinite sequence of prime numbers. + """ + # Maps composites to primes witnessing their compositeness. + # This is memory efficient, as the sieve is not "run forward" + # indefinitely, but only as long as required by the current + # number being tested. + # + D = {} + + # The running integer that's checked for primeness + q = 2 + + while True: + if q not in D: + # q is a new prime. + # Yield it and mark its first multiple that isn't + # already marked in previous iterations + # + yield q + D[q * q] = [q] + else: + # q is composite. D[q] is the list of primes that + # divide it. Since we've reached q, we no longer + # need it in the map, but we'll mark the next + # multiples of its witnesses to prepare for larger + # numbers + # + for p in D[q]: + D.setdefault(p + q, []).append(p) + del D[q] + + q += 1 diff --git a/src/test_balanced_bst.py b/src/test_balanced_bst.py index d6c1906..fc6e33b 100644 --- a/src/test_balanced_bst.py +++ b/src/test_balanced_bst.py @@ -711,3 +711,4 @@ def test_right_left_root_right_left_add_right(filled_bst_rot_rl): assert a.root.right.value == 15 assert a.root.right.left.value == 14 assert a.root.right.right.value == 20 + diff --git a/src/test_hash_table.py b/src/test_hash_table.py index 60e1e16..d46e8ec 100644 --- a/src/test_hash_table.py +++ b/src/test_hash_table.py @@ -2,3 +2,55 @@ from hash_table import HashTable import pytest + + +@pytest.fixture +def colin_ben_filled_hash_table(): + """Fixture for a filled hash table using the words from a dictionary text file hashed with the Colin-Ben hashing algorithm.""" + test_table = HashTable('colin-ben') + for line in open('/usr/share/dict/words'): + test_table.set(line, line) + return test_table + + +@pytest.fixture +def additive_filled_hash_table(): + """Fixture for a filled hash table using the words from a dictionary text file hashed with the additive technique.""" + test_table = HashTable('additive') + for line in open('/usr/share/dict/words'): + test_table.set(line, line) + return test_table + + +@pytest.fixture +def colin_ben_filled_hash_table_tiny(): + """Fixture for a filled hash table using the words from a dictionary text file hashed with the Colin-Ben hashing algorithm.""" + test_table = HashTable('colin-ben') + test_table.set('tornado', 25) + test_table.set('kangaroo', 50) + return test_table + + +def test_table_correct(colin_ben_filled_hash_table): + """Testing that get works correctly.""" + count = 0 + for line in open('/usr/share/dict/words'): + count += 1 + if colin_ben_filled_hash_table.get(line) != line: + assert False + assert True + + +def test_table_correct2(additive_filled_hash_table): + """Testing that get works correctly.""" + # import pdb; pdb.set_trace() + for line in open('/usr/share/dict/words'): + if additive_filled_hash_table.get(line) != line: + assert False + assert True + + +def test_table_correct_tiny(colin_ben_filled_hash_table_tiny): + """Testing that get works correctly.""" + assert colin_ben_filled_hash_table_tiny.get('kangaroo') == 50 + assert colin_ben_filled_hash_table_tiny.get('koala') == 'Key not in hash table.' From 14cf4b7b1e1fb4c5c20ced0619bb136ecfeb43ef Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Wed, 25 Jan 2017 16:15:28 -0800 Subject: [PATCH 66/87] added readme and deleted corpse code. --- src/hash_table.py | 76 ++++++++++------------------------------------- 1 file changed, 16 insertions(+), 60 deletions(-) diff --git a/src/hash_table.py b/src/hash_table.py index 03b24f6..6b8a1d2 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -1,4 +1,4 @@ - +"""Hash table module.""" # HASH TABLE (HT) # # CodeFellows 401d5 @@ -19,6 +19,10 @@ a specific bucket. As the content of the key always maps to a specific hash value, the key's value may be accessed quickly. + + - get(key) - should return the value stored with the given key + - set(key, val) - should store the given val using the given key + - _hash(key) - should hash the key provided (note that this is an internal api) """ @@ -86,59 +90,6 @@ def _colin_ben_hash(self, key): # a_sum = sum([(each[0] << next(sieve)) * (each[1] << next(sieve)) for each in ords]) # return a_sum % self._num_buckets -# def genPrimes(): -# def isPrime(n): -# if n % 2 == 0: return n == 2 -# d = 3 -# while d * d <= n: -# if n % d == 0: return False -# d += 2 -# return True -# def init(): # change to Sieve of Eratosthenes -# ps, qs, sieve = [], [], [True] * 50000 -# p, m = 3, 0 -# while p * p <= 100000: -# if isPrime(p): -# ps.insert(0, p) -# qs.insert(0, p + (p-1) / 2) -# m += 1 -# p += 2 -# for i in xrange(m): -# for j in xrange(qs[i], 50000, ps[i]): -# sieve[j] = False -# return m, ps, qs, sieve -# def advance(m, ps, qs, sieve, bottom): -# for i in xrange(50000): sieve[i] = True -# for i in xrange(m): -# qs[i] = (qs[i] - 50000) % ps[i] -# p = ps[0] + 2 -# while p * p <= bottom + 100000: -# if isPrime(p): -# ps.insert(0, p) -# qs.insert(0, (p*p - bottom - 1)/2) -# m += 1 -# p += 2 -# for i in xrange(m): -# for j in xrange(qs[i], 50000, ps[i]): -# sieve[j] = False -# return m, ps, qs, sieve -# m, ps, qs, sieve = init() -# bottom, i = 0, 1 -# yield 2 -# while True: -# if i == 50000: -# bottom = bottom + 100000 -# m, ps, qs, sieve = advance(m, ps, qs, sieve, bottom) -# i = 0 -# elif sieve[i]: -# yield bottom + i + i + 1 -# i += 1 -# else: i += 1 - -# Sieve of Eratosthenes -# Code by David Eppstein, UC Irvine, 28 Feb 2002 -# http://code.activestate.com/recipes/117119/ - def gen_primes(): """ Generate an infinite sequence of prime numbers. @@ -149,27 +100,32 @@ def gen_primes(): # number being tested. # D = {} - + # The running integer that's checked for primeness q = 2 - + while True: if q not in D: # q is a new prime. # Yield it and mark its first multiple that isn't # already marked in previous iterations - # + # yield q D[q * q] = [q] else: # q is composite. D[q] is the list of primes that # divide it. Since we've reached q, we no longer - # need it in the map, but we'll mark the next + # need it in the map, but we'll mark the next # multiples of its witnesses to prepare for larger # numbers - # + # for p in D[q]: D.setdefault(p + q, []).append(p) del D[q] - + q += 1 + +# Gen Primes function: +# Sieve of Eratosthenes +# Code by David Eppstein, UC Irvine, 28 Feb 2002 +# http://code.activestate.com/recipes/117119/ From ac7749de6ccc9f4de647281b0981dcb89cd7f504 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Wed, 25 Jan 2017 16:17:22 -0800 Subject: [PATCH 67/87] added coverage and info to readme. --- README.MD | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/README.MD b/README.MD index eaae067..6871606 100644 --- a/README.MD +++ b/README.MD @@ -1,32 +1,34 @@ #Summary -The assignment was to implement a [Weighted Graph](https://codefellows.github.io/sea-python-401d5/assignments/graph_3_weighted.html) -in Python containing the following methods: +The HashTable is a data structure that implements an +assosiative array. The structure maps keys to values +in such a way that values may be accessed in O(1) time. +This is done through use of a hashing function. +The hashing function maps the content of the key to +a specific bucket. As the content of the key always +maps to a specific hash value, the key's value may +be accessed quickly. - - * g.nodes(): return a list of all nodes in the graph - * g.edges(): return a list of all edges in the graph - * g.add_node(n): adds a new node 'n' to the graph - * g.add_edge(n1, n2): adds a new edge to the graph connecting 'n1' and 'n2', if either n1 or n2 are not already present in the graph, they should be added. - * g.del_node(n): deletes the node 'n' from the graph, raises an error if no such node exists - * g.del_edge(n1, n2): deletes the edge connecting 'n1' and 'n2' from the graph, raises an error if no such edge exists - * g.has_node(n): True if node 'n' is contained in the graph, False if not. - * g.neighbors(n): returns the list of all nodes connected to 'n' by edges, raises an error if n is not in g - * g.adjacent(n1, n2): returns True if there is an edge connecting n1 and n2, False if not, raises an error if either of the supplied nodes are not in g - * g.depth_first_traversal(start): Returns the path list for the entire graph with a depth first traversal. - * g.breadth_first_travers(start): Returns the path list for the entire graph with a breadth first traversal. + - get(key) - should return the value stored with the given key + - set(key, val) - should store the given val using the given key + - _hash(key) - should hash the key provided (note that this is an internal api) # Coverage: +----------------------------------------------------------- +---------- coverage: platform darwin, python 2.7.12-final-0 ---------- +Name Stmts Miss Cover Missing +----------------------------------------------------------- +src/hash_table.py 47 2 96% 54, 58 +----------------------------------------------------------- ---------- coverage: platform darwin, python 3.5.2-final-0 ----------- - - -| Name | Stmts | Miss | Cover | -| ----------------------- | ----- | ---- | ----- | -| weighted_graph.py | 78 | 3 | 96% | -| test_weighted_graph.py | 178 | 0 | 100% | -| ----------------------- | --- | -- | ---- | -| TOTAL | 256 | 3 | 98% | - - +Name Stmts Miss Cover Missing +----------------------------------------------------------- +src/hash_table.py 47 2 96% 54, 58 +----------------------------------------------------------- + +Gen Primes function used: +Sieve of Eratosthenes +Code by David Eppstein, UC Irvine, 28 Feb 2002 +http://code.activestate.com/recipes/117119/ From e89d48e0590f6519a833f872ac7456a62b0b6ee1 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Wed, 25 Jan 2017 17:26:58 -0800 Subject: [PATCH 68/87] started the docstrings. --- src/test_trie.py | 1 + src/trie.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/test_trie.py create mode 100644 src/trie.py diff --git a/src/test_trie.py b/src/test_trie.py new file mode 100644 index 0000000..712bd0e --- /dev/null +++ b/src/test_trie.py @@ -0,0 +1 @@ +"""Trie module.""" \ No newline at end of file diff --git a/src/trie.py b/src/trie.py new file mode 100644 index 0000000..c6d3c6b --- /dev/null +++ b/src/trie.py @@ -0,0 +1,18 @@ + + +""" + insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. + contains(self, string): will return True if the string is in the trie, False if not. + size(self): will return the total number of words contained within the trie. 0 if empty. + remove(self, string): will remove the given string from the trie. If the word doesn’t exist, will raise an appropriate exception. +""" + +class Node(object): + """Node class.""" + + def __init__(self, value=None, left=None, right=None): + """Init of the Node class.""" + self.value = value + self.node = node + self.right = right + self.parent = None From a154a1d220ccdcd199d031e79543bbe2990ee35b Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Wed, 25 Jan 2017 19:52:33 -0800 Subject: [PATCH 69/87] fix for deletion of root child. --- src/balanced_bst.py | 79 +++++++++++++++++++++++++++------------------ src/bst.py | 4 +-- 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/balanced_bst.py b/src/balanced_bst.py index 312b63d..a3fe063 100644 --- a/src/balanced_bst.py +++ b/src/balanced_bst.py @@ -268,7 +268,7 @@ def delete(self, val): self._balance_tree() return - if val == parent_of_del.right.value: + if parent_of_del.right and val == parent_of_del.right.value: if not min_parent: parent_of_del.right = None self.counter -= 1 @@ -303,8 +303,9 @@ def delete(self, val): if del_node: parent_of_del.right.parent = parent_of_del self.counter -= 1 + self._balance_tree() - elif val == parent_of_del.left.value: + elif parent_of_del.left and val == parent_of_del.left.value: if not min_parent: parent_of_del.left = None self.counter -= 1 @@ -339,7 +340,7 @@ def delete(self, val): if del_node: parent_of_del.left.parent = parent_of_del self.counter -= 1 - self._balance_tree() + self._balance_tree() def _find_min_parent(self, vertex, side): """Find the parent of the replacement node, given the parent of the delete node.""" @@ -428,12 +429,14 @@ def _right_rotation(self, node): def _left_right_rotation(self, node): """Try left right rotation on node.""" - if self._calc_balance(node.left) > 0: + # if self._calc_balance(node.left) > 0: + if node.left.right.left: vertex = node left_head = node.left right_sub = node.left.right - if node.left.right.left: - switcher = node.left.right.left + switcher = node.left.right.left + # if node.left.right.left: + # switcher = node.left.right.left if vertex.parent: right_sub.parent = vertex.parent if vertex.parent.value > vertex.value: @@ -443,11 +446,13 @@ def _left_right_rotation(self, node): else: self.root = right_sub right_sub.parent = None - if node.left.right.left: - switcher.parent = left_head + switcher.parent = left_head + # if node.left.right.left: + # switcher.parent = left_head left_head.parent = right_sub - if node.left.right.left: - left_head.right = switcher + left_head.right = switcher + # if node.left.right.left: + # left_head.right = switcher right_sub.right = vertex right_sub.left = left_head vertex.parent = right_sub @@ -456,8 +461,9 @@ def _left_right_rotation(self, node): vertex = node left_head = node.left right_sub = node.left.right - if node.left.right.right: - switcher = node.left.right.right + switcher = node.left.right.right + # if node.left.right.right: + # switcher = node.left.right.right if vertex.parent: right_sub.parent = vertex.parent if vertex.parent.value > vertex.value: @@ -467,10 +473,12 @@ def _left_right_rotation(self, node): else: self.root = right_sub right_sub.parent = None - if node.left.right.right: - switcher.parent = vertex - if node.left.right.right: - vertex.left = switcher + switcher.parent = vertex + vertex.left = switcher + # if node.left.right.right: + # switcher.parent = vertex + # if node.left.right.right: + # vertex.left = switcher vertex.parent = right_sub right_sub.right = vertex right_sub.left = left_head @@ -479,12 +487,14 @@ def _left_right_rotation(self, node): def _right_left_rotation(self, node): """Try right left rotation on node.""" - if self._calc_balance(node.right) < 0: + if node.right.left.left: + # if self._calc_balance(node.right) < 0: vertex = node right_head = node.right left_sub = node.right.left - if node.right.left.left: - switcher = node.right.left.left + switcher = node.right.left.left + # if node.right.left.left: + # switcher = node.right.left.left if vertex.parent: left_sub.parent = vertex.parent if vertex.parent.value > vertex.value: @@ -494,24 +504,27 @@ def _right_left_rotation(self, node): else: self.root = left_sub left_sub.parent = None - if node.right.left.left: - switcher.parent = right_head + switcher.parent = right_head + # if node.right.left.left: + # switcher.parent = right_head right_head.parent = vertex right_head.left = None left_sub.right = right_head left_sub.left = vertex vertex.parent = left_sub - if node.right.left.left: - vertex.right = switcher - else: - vertex.right = None + vertex.right = switcher + # if node.right.left.left: + # vertex.right = switcher + # else: + # vertex.right = None else: - import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() vertex = node right_head = node.right left_sub = node.right.left - if node.right.left.right: - switcher = node.right.left.right + switcher = node.right.left.right + # if node.right.left.right: + # switcher = node.right.left.right if vertex.parent: left_sub.parent = vertex.parent if vertex.parent.value > vertex.value: @@ -521,14 +534,16 @@ def _right_left_rotation(self, node): else: self.root = left_sub left_sub.parent = None - if node.right.left.right: - switcher.parent = right_head + switcher.parent = right_head + # if node.right.left.right: + # switcher.parent = right_head vertex.right = None vertex.parent = left_sub left_sub.left = vertex left_sub.right = right_head - if node.right.left.right: - right_head.left = switcher + right_head.left = switcher + # if node.right.left.right: + # right_head.left = switcher right_head.parent = left_sub def _post_order_node(self): diff --git a/src/bst.py b/src/bst.py index 79d3bbe..e8bba13 100644 --- a/src/bst.py +++ b/src/bst.py @@ -249,7 +249,7 @@ def delete(self, val): else: return - if val == parent_of_del.right.value: + if parent_of_del.right and val == parent_of_del.right.value: if not min_parent: parent_of_del.right = None self.counter -= 1 @@ -271,7 +271,7 @@ def delete(self, val): parent_of_del.right = del_node self.counter -= 1 - elif val == parent_of_del.left.value: + elif parent_of_del.left and val == parent_of_del.left.value: if not min_parent: parent_of_del.left = None self.counter -= 1 From e8d30bb8a88b8839c02799889ce5afce666e6348 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 26 Jan 2017 11:59:18 -0800 Subject: [PATCH 70/87] trie functions, untested. --- src/trie.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/trie.py diff --git a/src/trie.py b/src/trie.py new file mode 100644 index 0000000..469ee4b --- /dev/null +++ b/src/trie.py @@ -0,0 +1,61 @@ +class Node(object): + + def __init__(self, val): + self.val = None + self.nodes = {} + + +class Trie(object): + + def __init(self): + self.root = Node('*') + self.size = 0 + + def insert(self, word): + node = self.root + new_node = None + new_word = False + for each in word: + if each in node.nodes: + node = node.nodes[each] + continue + new_word = True + new_node = Node('each') + node.nodes[each] = new_node + node = new_node + if new_word: + self.size += 1 + node['$'] = None + + def contains(self, word): + node = self.root + for each in word: + if each in node.nodes: + node = node.nodes[each] + else: + return False + if '$' in node.nodes: + return True + return False + + def size(self, size): + return self.size + + def remove(self, word): + node_list = [] + node = self.root + for each in word: + if each in node.nodes: + node_list.append(node) + node = node.nodes[each] + continue + last = node_list.pop() + if '$' not in last.nodes: + return + del last.nodes['$'] + for i in range(len(node_list)): + last_val = last.val + last = node_list.pop() + if len(last.nodes) > 1: + return + del last.nodes[last_val] From e5542e360e94e25079fe5131e709e5f90f277807 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 26 Jan 2017 13:07:19 -0800 Subject: [PATCH 71/87] debugging trie. --- src/trie.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/trie.py b/src/trie.py index 0e50e67..1bac199 100644 --- a/src/trie.py +++ b/src/trie.py @@ -8,14 +8,14 @@ class Node(object): - def __init__(self, val): - self.val = None + def __init__(self, val=None): + self.val = val self.nodes = {} class Trie(object): - def __init(self): + def __init__(self): self.root = Node('*') self.size = 0 @@ -28,12 +28,12 @@ def insert(self, word): node = node.nodes[each] continue new_word = True - new_node = Node('each') + new_node = Node(each) node.nodes[each] = new_node node = new_node if new_word: self.size += 1 - node['$'] = None + node.nodes['$'] = None def contains(self, word): node = self.root From f2931d93ed33258244ea2c9fdd42b2ed91eef147 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 26 Jan 2017 14:42:33 -0800 Subject: [PATCH 72/87] debugged remove method. --- src/trie.py | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/trie.py b/src/trie.py index 1bac199..83e80ab 100644 --- a/src/trie.py +++ b/src/trie.py @@ -17,7 +17,7 @@ class Trie(object): def __init__(self): self.root = Node('*') - self.size = 0 + self._size = 0 def insert(self, word): node = self.root @@ -32,7 +32,7 @@ def insert(self, word): node.nodes[each] = new_node node = new_node if new_word: - self.size += 1 + self._size += 1 node.nodes['$'] = None def contains(self, word): @@ -46,17 +46,16 @@ def contains(self, word): return True return False - def size(self, size): - return self.size + def size(self): + return self._size def remove(self, word): node_list = [] node = self.root for each in word: if each in node.nodes: - node_list.append(node) + node_list.append(node.nodes[each]) node = node.nodes[each] - continue last = node_list.pop() if '$' not in last.nodes: return @@ -64,17 +63,9 @@ def remove(self, word): for i in range(len(node_list)): last_val = last.val last = node_list.pop() + if '$' in last.nodes: + break if len(last.nodes) > 1: - return + del last.nodes[last_val] + break del last.nodes[last_val] - - -# class Node(object): -# """Node class.""" - -# def __init__(self, value=None, left=None, right=None): -# """Init of the Node class.""" -# self.value = value -# self.node = node -# self.right = right -# self.parent = None From f77e8e1dd8135ad8dea22a34ea69573ebfa9d9e0 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Thu, 26 Jan 2017 15:04:20 -0800 Subject: [PATCH 73/87] decremented size, added doc strings. --- src/trie.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/trie.py b/src/trie.py index 83e80ab..746f924 100644 --- a/src/trie.py +++ b/src/trie.py @@ -1,3 +1,5 @@ +"""Module for Trie tree.""" + """ insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. contains(self, string): will return True if the string is in the trie, False if not. @@ -7,19 +9,24 @@ class Node(object): + """Node Class contains a value and a dictionary of nodes.""" def __init__(self, val=None): + """Initialize the Node class with val and empty nodes dictionary.""" self.val = val self.nodes = {} class Trie(object): + """Trie class, which is the Trie tree.""" def __init__(self): + """Initialize the Trie class with root Node with ('*') and size of 0.""" self.root = Node('*') self._size = 0 def insert(self, word): + """Insert method, which takes a word and inserts each letter of the word into the Trie, with pointer to next Node or $ if end.""" node = self.root new_node = None new_word = False @@ -36,6 +43,7 @@ def insert(self, word): node.nodes['$'] = None def contains(self, word): + """The contains method returns True if the word is found in the Trie tree, or False if not.""" node = self.root for each in word: if each in node.nodes: @@ -47,9 +55,11 @@ def contains(self, word): return False def size(self): + """The size method returns the number of words in the Trie.""" return self._size def remove(self, word): + """The remove method removes the word from the Trie.""" node_list = [] node = self.root for each in word: @@ -64,8 +74,10 @@ def remove(self, word): last_val = last.val last = node_list.pop() if '$' in last.nodes: + self._size -= 1 break if len(last.nodes) > 1: del last.nodes[last_val] + self._size -= 1 break del last.nodes[last_val] From 561ee4d27f87584dcacbaa9fd18a99de329e3eda Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Thu, 26 Jan 2017 15:09:31 -0800 Subject: [PATCH 74/87] changed travis yml file to stop email notications. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1fc8989..ccd3ca9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,3 +12,7 @@ install: # command to run tests script: - py.test --cov=. --cover-report term-missing + +# notifications +notifications: + email: false \ No newline at end of file From 7f8f6e46d7ef73db6a49e9e042a6ed89d853c4b9 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 26 Jan 2017 15:12:40 -0800 Subject: [PATCH 75/87] credentials. --- src/hash_table.py | 1 + src/trie.py | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/hash_table.py b/src/hash_table.py index 6b8a1d2..d0ac755 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -1,4 +1,5 @@ """Hash table module.""" + # HASH TABLE (HT) # # CodeFellows 401d5 diff --git a/src/trie.py b/src/trie.py index 746f924..927685a 100644 --- a/src/trie.py +++ b/src/trie.py @@ -1,11 +1,14 @@ """Module for Trie tree.""" -""" - insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. - contains(self, string): will return True if the string is in the trie, False if not. - size(self): will return the total number of words contained within the trie. 0 if empty. - remove(self, string): will remove the given string from the trie. If the word doesn’t exist, will raise an appropriate exception. -""" +# TRIE TREE (TT) +# +# CodeFellows 401d5 +# Submission Date: +# +# Authors: Colin Lamont +# Ben Shields +# +# URL: class Node(object): @@ -20,6 +23,13 @@ def __init__(self, val=None): class Trie(object): """Trie class, which is the Trie tree.""" + """ + insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. + contains(self, string): will return True if the string is in the trie, False if not. + size(self): will return the total number of words contained within the trie. 0 if empty. + remove(self, string): will remove the given string from the trie. If the word doesn’t exist, will raise an appropriate exception. + """ + def __init__(self): """Initialize the Trie class with root Node with ('*') and size of 0.""" self.root = Node('*') From 599feb00b39205dda04331f49c6cd623d65b556b Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 26 Jan 2017 15:59:21 -0800 Subject: [PATCH 76/87] beginning implementation of traversal of trie. --- src/test_trie_trav.py | 0 src/trie_trav.py | 105 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/test_trie_trav.py create mode 100644 src/trie_trav.py diff --git a/src/test_trie_trav.py b/src/test_trie_trav.py new file mode 100644 index 0000000..e69de29 diff --git a/src/trie_trav.py b/src/trie_trav.py new file mode 100644 index 0000000..d7a135f --- /dev/null +++ b/src/trie_trav.py @@ -0,0 +1,105 @@ +"""Module for Trie tree with traversal.""" + +# TRIE TREE TRAVERSAL (TTT) +# +# CodeFellows 401d5 +# Submission Date: +# +# Authors: Colin Lamont +# Ben Shields +# +# URL: + +from collections import OrderedDict + + +class Node(object): + """Node Class contains a value and a dictionary of nodes.""" + + def __init__(self, val=None): + """Initialize the Node class with val and empty nodes dictionary.""" + self.val = val + self.nodes = OrderedDict() + + +class Trie(object): + """Trie class, which is the Trie tree.""" + + """ + insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. + contains(self, string): will return True if the string is in the trie, False if not. + size(self): will return the total number of words contained within the trie. 0 if empty. + remove(self, string): will remove the given string from the trie. If the word doesn’t exist, will raise an appropriate exception. + """ + + def __init__(self): + """Initialize the Trie class with root Node with ('*') and size of 0.""" + self.root = Node('*') + self._size = 0 + + def insert(self, word): + """Insert method, which takes a word and inserts each letter of the word into the Trie, with pointer to next Node or $ if end.""" + node = self.root + new_node = None + new_word = False + for each in word: + if each in node.nodes: + node = node.nodes[each] + continue + new_word = True + new_node = Node(each) + node.nodes[each] = new_node + node = new_node + if new_word: + self._size += 1 + node.nodes['$'] = None + + def contains(self, word): + """The contains method returns True if the word is found in the Trie tree, or False if not.""" + node = self.root + for each in word: + if each in node.nodes: + node = node.nodes[each] + else: + return False + if '$' in node.nodes: + return True + return False + + def size(self): + """The size method returns the number of words in the Trie.""" + return self._size + + def remove(self, word): + """The remove method removes the word from the Trie.""" + node_list = [] + node = self.root + for each in word: + if each in node.nodes: + node_list.append(node.nodes[each]) + node = node.nodes[each] + last = node_list.pop() + if '$' not in last.nodes: + return + del last.nodes['$'] + for i in range(len(node_list)): + last_val = last.val + last = node_list.pop() + if '$' in last.nodes: + self._size -= 1 + break + if len(last.nodes) > 1: + del last.nodes[last_val] + self._size -= 1 + break + del last.nodes[last_val] + + def traveral(self, start=None): + node = self.root + output = [] + for each in start: + if each in node.nodes: + node = node.nodes[each] + for each in node.nodes: + trav_node = None + for each \ No newline at end of file From 10574d56d90f1b1a52ba9b426499f58cedc5d8ea Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Thu, 26 Jan 2017 16:55:40 -0800 Subject: [PATCH 77/87] start of the trie traversal. --- src/trie_trav.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/trie_trav.py b/src/trie_trav.py index d7a135f..3168021 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -94,12 +94,18 @@ def remove(self, word): break del last.nodes[last_val] - def traveral(self, start=None): + def traversal(self, start=None): + """The traversal method does a depth first traversal of the trie to find instances of start and return the rest.""" node = self.root output = [] for each in start: + import pdb; pdb.set_trace() if each in node.nodes: node = node.nodes[each] - for each in node.nodes: - trav_node = None - for each \ No newline at end of file + for letter in node.nodes: + for endword in node.nodes[letter].nodes: + print(letter) + print(endword) + output.append(letter) + output.append(endword) + return output From 8d207e7e1b91e30cfd08bf70940996b38ae987dd Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 26 Jan 2017 16:55:44 -0800 Subject: [PATCH 78/87] tests for trie_trav with parameters and fixtures. --- src/test_trie_trav.py | 76 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/test_trie_trav.py b/src/test_trie_trav.py index e69de29..d23f686 100644 --- a/src/test_trie_trav.py +++ b/src/test_trie_trav.py @@ -0,0 +1,76 @@ +from trie_trav import Trie +import pytest + +SIMPLE_INPUT = ['abort', 'tony', 'borg', 'russia'] +MODERATE_INPUT = ['fast', 'faster', 'fastest', 'fastener', 'breakfasttime'] +COMPLEX_INPUT = ['aaaaaa', 'aardvark', 'aaraal', 'aapppp', 'aapear', 'tornado', 'ado', 'tor', 'to', 'o', 'oo', 'oooo', 'elephant', 'elevate', 'elephants'] + +PARAMS_SIMPLE = [ + ('rus', ['s', 'i', 'a']), + ('o', ['r', 't', 'n', 'y', 'r', 'g']), + ('a', ['b', 'o', 'r', 't']), + ('r', ['t', 'g']) +] + +PARAMS_MODERATE = [ + ('fast', ['e', 'r', 's', 't', 'n', 'e', 'r', 't', 'i', 'm', 'e']), + ('f', ['a', 's', 't', 'e', 'r', 's', 't', 'n', 'e', 'r', 'a', 's', 't', 't', 'i', 'm', 'e']), + ('a', ['s', 't', 'e', 'r', 's', 't', 'n', 'e', 'r', 'k', 'f', 'a', 's', 't', 'i', 'm', 'e']), + ('faste', ['r', 's', 't', 'n', 'e', 'r']), + ('t', ['r', 'r', 's', 't', 'n', 'e', 'r', 't', 'i', 'm', 'e']), + ('faster', []), +] + +PARAMS_COMPLEX = [ + ('a', ['d', 'o', 'a', 'a', 'a', 'a', 'a', 'r', 'd', 'v', 'a', 'r', 'k', 'a', 'a', 'l', 'p', 'p', 'p', 'p', 'e', 'a', 'r', 'd', 'o', 'n', 't', 's', 't', 'e']), + ('', []), + ('', []), +] + + +@pytest.fixture +def simple_trie(): + """Trie with simple input.""" + a = Trie() + for each in SIMPLE_INPUT: + a.insert(each) + return a + + +@pytest.fixture +def moderate_trie(): + """Trie with moderate input.""" + a = Trie() + for each in MODERATE_INPUT: + a.insert(each) + return a + + +@pytest.fixture +def complex_trie(): + """Trie with complex input.""" + a = Trie() + for each in COMPLEX_INPUT: + a.insert(each) + return a + + +@pytest.mark.parametrize('n, result', PARAMS_SIMPLE) +def test_traversal_simple(simple_trie, n, result): + """Test traversal with input 'o'.""" + g = simple_trie.traversal(n) + assert list(g) == result + + +@pytest.mark.parametrize('n, result', PARAMS_MODERATE) +def test_traversal_moderate(simple_trie, n, result): + """Test traversal with input 'o'.""" + g = simple_trie.traversal(n) + assert list(g) == result + + +@pytest.mark.parametrize('n, result', PARAMS_COMPLEX) +def test_traversal_complex(simple_trie, n, result): + """Test traversal with input 'o'.""" + g = simple_trie.traversal(n) + assert list(g) == result From da15f5e448625568ad1f00be6729ad7321bc4fdb Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Thu, 26 Jan 2017 17:30:51 -0800 Subject: [PATCH 79/87] while loop in traversal. --- src/trie_trav.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/trie_trav.py b/src/trie_trav.py index 3168021..f9c9a68 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -98,14 +98,25 @@ def traversal(self, start=None): """The traversal method does a depth first traversal of the trie to find instances of start and return the rest.""" node = self.root output = [] - for each in start: - import pdb; pdb.set_trace() - if each in node.nodes: - node = node.nodes[each] - for letter in node.nodes: - for endword in node.nodes[letter].nodes: - print(letter) - print(endword) - output.append(letter) - output.append(endword) - return output + while True: + if node.nodes: + for each in node.nodes: + yield each + break + + for letter in start: + # import pdb; pdb.set_trace() + while True: + if node.nodes: + for each in node.nodes: + yield each + + if each in node.nodes: + node = node.nodes[each] + for letter in node.nodes: + for endword in node.nodes[letter].nodes: + print(letter) + print(endword) + output.append(letter) + output.append(endword) + return output From 565fc66a0d0e3ae869fddbbc9a17b9b57426a2da Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Fri, 27 Jan 2017 09:20:36 -0800 Subject: [PATCH 80/87] recursive traversal method for trie. --- src/trie_trav.py | 66 ++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/src/trie_trav.py b/src/trie_trav.py index f9c9a68..c2b3b7c 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -96,27 +96,45 @@ def remove(self, word): def traversal(self, start=None): """The traversal method does a depth first traversal of the trie to find instances of start and return the rest.""" - node = self.root - output = [] - while True: - if node.nodes: - for each in node.nodes: - yield each - break - - for letter in start: - # import pdb; pdb.set_trace() - while True: - if node.nodes: - for each in node.nodes: - yield each - - if each in node.nodes: - node = node.nodes[each] - for letter in node.nodes: - for endword in node.nodes[letter].nodes: - print(letter) - print(endword) - output.append(letter) - output.append(endword) - return output + if not start: + self._traversal(self.root) + else: + + + + # node = self.root + # output = [] + # while True: + # if node.nodes: + # for each in node.nodes: + # yield each + # break + + # for letter in start: + # # import pdb; pdb.set_trace() + # while True: + # if node.nodes: + # for each in node.nodes: + # yield each + + # if each in node.nodes: + # node = node.nodes[each] + # for letter in node.nodes: + # for endword in node.nodes[letter].nodes: + # print(letter) + # print(endword) + # output.append(letter) + # output.append(endword) + # return output + + def _find_start(self, start): + """Return the next instance of start string.""" + + def _traversal(self, node): + """Recursive helper method for traversal. Yields value at node.""" + if len(node.nodes) == 1 and '$' in node.nodes: + return + else: + for each in node.nodes: + self._traversal(node.nodes[each]) + return node.val \ No newline at end of file From c2c6332b833ec7084be695992cd5bc732d6829bc Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sat, 28 Jan 2017 16:17:10 -0800 Subject: [PATCH 81/87] full traversal, not specific to start. --- src/trie_trav.py | 128 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 39 deletions(-) diff --git a/src/trie_trav.py b/src/trie_trav.py index c2b3b7c..6104199 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -95,46 +95,96 @@ def remove(self, word): del last.nodes[last_val] def traversal(self, start=None): - """The traversal method does a depth first traversal of the trie to find instances of start and return the rest.""" + """A generator yielding values in the tree in depth first order.""" if not start: - self._traversal(self.root) + stack = [] + stack.append(self.root) + while len(stack) > 0: + node = stack.pop() + if node.val != '*': + yield node.val + items = node.nodes.items() + items = reversed(items) + nodes = OrderedDict(items) + for each in nodes: + if len(node.nodes) == 1 and '$' in node.nodes: + break + stack.append(nodes[each]) else: - - - - # node = self.root - # output = [] - # while True: - # if node.nodes: - # for each in node.nodes: - # yield each - # break - - # for letter in start: - # # import pdb; pdb.set_trace() - # while True: - # if node.nodes: - # for each in node.nodes: - # yield each - - # if each in node.nodes: - # node = node.nodes[each] - # for letter in node.nodes: - # for endword in node.nodes[letter].nodes: - # print(letter) - # print(endword) - # output.append(letter) - # output.append(endword) - # return output - - def _find_start(self, start): - """Return the next instance of start string.""" + word = '' + stack = [] + branch_node = None + to_yield = False + stack.append(self.root) + while len(stack) > 0: + node = stack.pop() + word += node.val + if to_yield: + yield node.val + if start in word: + to_yield = True + branch_node = node + items = node.nodes.items() + items = reversed(items) + nodes = OrderedDict(items) + for each in nodes: + if len(node.nodes) == 1 and '$' in node.nodes: + break + stack.append(nodes[each]) + + stack = [] + stack.append(self.root) + while len(stack) > 0: + node = stack.pop() + if node.val != '*': + yield node.val + items = node.nodes.items() + items = reversed(items) + nodes = OrderedDict(items) + for each in nodes: + if len(node.nodes) == 1 and '$' in node.nodes: + break + stack.append(nodes[each]) def _traversal(self, node): - """Recursive helper method for traversal. Yields value at node.""" - if len(node.nodes) == 1 and '$' in node.nodes: - return - else: - for each in node.nodes: - self._traversal(node.nodes[each]) - return node.val \ No newline at end of file + + stack = [] + stack.append(node) + while len(stack) > 0: + node = stack.pop() + if node.val != '*': + yield node.val + items = node.nodes.items() + items = reversed(items) + nodes = OrderedDict(items) + for each in nodes: + if len(node.nodes) == 1 and '$' in node.nodes: + break + stack.append(nodes[each]) + + + # def traversal(self, start=None): + # """The traversal method does a depth first traversal of the trie to find instances of start and return the rest.""" + # g = self._traversal(self.root) + # yield next(g) + + # def _find_start(self, start): + # """Return the next instance of start string.""" + # pass + + # def _traversal(self, node): + # """Recursive helper method for traversal. Yields value at node.""" + # print('111111111111111111111') + # import pdb; pdb.set_trace() + # if len(node.nodes) == 1 and '$' in node.nodes: + # print('2222222222222222222') + # yield node.val + # return + # yield node.val + # for each in node.nodes: + # print('333333333333333333') + # print('each', each) + # print('node.nodes[each]', node.nodes[each]) + # print(node.nodes) + # h = self._traversal(node.nodes[each]) + # yield next(h) From 7fd8075b1574abaffc21150c3e0511596206731d Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sat, 28 Jan 2017 19:14:23 -0800 Subject: [PATCH 82/87] tests for trie and commented out portions of traversal in trie trav. --- src/test_trie.py | 52 ++++++++++++++++++++++++++++++++++- src/trie.py | 2 +- src/trie_trav.py | 70 ++++++++++++++++++++++++------------------------ 3 files changed, 87 insertions(+), 37 deletions(-) diff --git a/src/test_trie.py b/src/test_trie.py index 712bd0e..bae96b7 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -1 +1,51 @@ -"""Trie module.""" \ No newline at end of file +"""Test module for Trie Tree.""" + +from trie import Trie +import pytest + + +SIMPLE_INPUT = ['abort', 'tony', 'borg', 'russia'] +MODERATE_INPUT = ['fast', 'faster', 'fastest', 'fastener', 'breakfasttime'] +COMPLEX_INPUT = ['aaaaaa', 'aardvark', 'aaraal', 'aapppp', 'aapear', 'tornado', 'ado', 'tor', 'to', 'o', 'oo', 'oooo', 'elephant', 'elevate', 'elephants'] + +PARAMS_CONTAINS_SIMPLE = [ + ('abort', True), + ('tony', True), + ('ab', False), + ('z', False), + ('a', False), + ('t', False) +] + + +@pytest.fixture +def simple_trie(): + """Trie with simple input.""" + a = Trie() + for each in SIMPLE_INPUT: + a.insert(each) + return a + + +@pytest.fixture +def moderate_trie(): + """Trie with moderate input.""" + a = Trie() + for each in MODERATE_INPUT: + a.insert(each) + return a + + +@pytest.fixture +def complex_trie(): + """Trie with complex input.""" + a = Trie() + for each in COMPLEX_INPUT: + a.insert(each) + return a + + +@pytest.mark.parametrize('n, result', PARAMS_CONTAINS_SIMPLE) +def test_contains_simple(simple_trie, n, result): + """Test contains function.""" + assert simple_trie.contains(n) == result diff --git a/src/trie.py b/src/trie.py index 927685a..be7fd03 100644 --- a/src/trie.py +++ b/src/trie.py @@ -27,7 +27,7 @@ class Trie(object): insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. contains(self, string): will return True if the string is in the trie, False if not. size(self): will return the total number of words contained within the trie. 0 if empty. - remove(self, string): will remove the given string from the trie. If the word doesn’t exist, will raise an appropriate exception. + remove(self, string): will remove the given string from the trie. If the word does not exist, will raise an appropriate exception """ def __init__(self): diff --git a/src/trie_trav.py b/src/trie_trav.py index 6104199..fce20fa 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -110,41 +110,41 @@ def traversal(self, start=None): if len(node.nodes) == 1 and '$' in node.nodes: break stack.append(nodes[each]) - else: - word = '' - stack = [] - branch_node = None - to_yield = False - stack.append(self.root) - while len(stack) > 0: - node = stack.pop() - word += node.val - if to_yield: - yield node.val - if start in word: - to_yield = True - branch_node = node - items = node.nodes.items() - items = reversed(items) - nodes = OrderedDict(items) - for each in nodes: - if len(node.nodes) == 1 and '$' in node.nodes: - break - stack.append(nodes[each]) - - stack = [] - stack.append(self.root) - while len(stack) > 0: - node = stack.pop() - if node.val != '*': - yield node.val - items = node.nodes.items() - items = reversed(items) - nodes = OrderedDict(items) - for each in nodes: - if len(node.nodes) == 1 and '$' in node.nodes: - break - stack.append(nodes[each]) + # else: + # word = '' + # stack = [] + # branch_node = None + # to_yield = False + # stack.append(self.root) + # while len(stack) > 0: + # node = stack.pop() + # word += node.val + # if to_yield: + # yield node.val + # if start in word: + # to_yield = True + # branch_node = node + # items = node.nodes.items() + # items = reversed(items) + # nodes = OrderedDict(items) + # for each in nodes: + # if len(node.nodes) == 1 and '$' in node.nodes: + # break + # stack.append(nodes[each]) + + # stack = [] + # stack.append(self.root) + # while len(stack) > 0: + # node = stack.pop() + # if node.val != '*': + # yield node.val + # items = node.nodes.items() + # items = reversed(items) + # nodes = OrderedDict(items) + # for each in nodes: + # if len(node.nodes) == 1 and '$' in node.nodes: + # break + # stack.append(nodes[each]) def _traversal(self, node): From 010774b3f1863d6d852e28df45d1a1adf3cbd37c Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sun, 29 Jan 2017 16:16:31 -0800 Subject: [PATCH 83/87] trie traversal --- src/trie_trav.py | 72 ++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/trie_trav.py b/src/trie_trav.py index fce20fa..8cbaa90 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -110,27 +110,27 @@ def traversal(self, start=None): if len(node.nodes) == 1 and '$' in node.nodes: break stack.append(nodes[each]) - # else: - # word = '' - # stack = [] - # branch_node = None - # to_yield = False - # stack.append(self.root) - # while len(stack) > 0: - # node = stack.pop() - # word += node.val - # if to_yield: - # yield node.val - # if start in word: - # to_yield = True - # branch_node = node - # items = node.nodes.items() - # items = reversed(items) - # nodes = OrderedDict(items) - # for each in nodes: - # if len(node.nodes) == 1 and '$' in node.nodes: - # break - # stack.append(nodes[each]) + else: + word = '' + stack = [] + branch_node = None + to_yield = False + stack.append(self.root) + while len(stack) > 0: + node = stack.pop() + word += node.val + if to_yield: + yield node.val + if start in word: + to_yield = True + branch_node = node + items = node.nodes.items() + items = reversed(items) + nodes = OrderedDict(items) + for each in nodes: + if len(node.nodes) == 1 and '$' in node.nodes: + break + stack.append(nodes[each]) # stack = [] # stack.append(self.root) @@ -146,21 +146,21 @@ def traversal(self, start=None): # break # stack.append(nodes[each]) - def _traversal(self, node): - - stack = [] - stack.append(node) - while len(stack) > 0: - node = stack.pop() - if node.val != '*': - yield node.val - items = node.nodes.items() - items = reversed(items) - nodes = OrderedDict(items) - for each in nodes: - if len(node.nodes) == 1 and '$' in node.nodes: - break - stack.append(nodes[each]) + # def _traversal(self, node): + + # stack = [] + # stack.append(node) + # while len(stack) > 0: + # node = stack.pop() + # if node.val != '*': + # yield node.val + # items = node.nodes.items() + # items = reversed(items) + # nodes = OrderedDict(items) + # for each in nodes: + # if len(node.nodes) == 1 and '$' in node.nodes: + # break + # stack.append(nodes[each]) # def traversal(self, start=None): From 66bb5df0863998818877c3c21ee2443f4410a824 Mon Sep 17 00:00:00 2001 From: William Benjamin Shields Date: Sun, 29 Jan 2017 17:22:01 -0800 Subject: [PATCH 84/87] generator yields all items after the first instance of the start word. --- src/trie_trav.py | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/trie_trav.py b/src/trie_trav.py index 8cbaa90..fac4bfb 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -111,19 +111,30 @@ def traversal(self, start=None): break stack.append(nodes[each]) else: - word = '' + compare = '' + index = 0 + comparing = False + found = False stack = [] - branch_node = None - to_yield = False stack.append(self.root) + # import pdb; pdb.set_trace() while len(stack) > 0: node = stack.pop() - word += node.val - if to_yield: + if node.val != '*' and found: yield node.val - if start in word: - to_yield = True - branch_node = node + if node.val == start[index]: + comparing = True + compare += start[index] + index += 1 + if index == len(start): + found = True + comparing = False + compare = '' + index = 0 + elif comparing: + comparing = False + compare = '' + index = 0 items = node.nodes.items() items = reversed(items) nodes = OrderedDict(items) @@ -131,6 +142,26 @@ def traversal(self, start=None): if len(node.nodes) == 1 and '$' in node.nodes: break stack.append(nodes[each]) + # word = '' + # stack = [] + # branch_node = None + # to_yield = False + # stack.append(self.root) + # while len(stack) > 0: + # node = stack.pop() + # word += node.val + # if to_yield: + # yield node.val + # if start in word: + # to_yield = True + # branch_node = node + # items = node.nodes.items() + # items = reversed(items) + # nodes = OrderedDict(items) + # for each in nodes: + # if len(node.nodes) == 1 and '$' in node.nodes: + # break + # stack.append(nodes[each]) # stack = [] # stack.append(self.root) From cb5556aa1c0d08acc9389b82764fc3fc35be166e Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 30 Jan 2017 08:54:06 -0800 Subject: [PATCH 85/87] updated insert and remove functions from the trie branch, and removed corpse code. --- src/trie_trav.py | 95 ++++++++---------------------------------------- 1 file changed, 15 insertions(+), 80 deletions(-) diff --git a/src/trie_trav.py b/src/trie_trav.py index fac4bfb..bb14ea4 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -37,15 +37,24 @@ def __init__(self): self.root = Node('*') self._size = 0 - def insert(self, word): - """Insert method, which takes a word and inserts each letter of the word into the Trie, with pointer to next Node or $ if end.""" +def insert(self, word): + """Insert method, which takes a word and inserts each letter of the word into the Trie, with pointer to next Node or $ if end.""" node = self.root new_node = None new_word = False + counter = 0 for each in word: if each in node.nodes: + if counter == (len(word) - 1) and '$' not in node.nodes: + new_word = True + new_node = Node(each) + node.nodes[each] = new_node + node = new_node + break + counter += 1 node = node.nodes[each] continue + counter += 1 new_word = True new_node = Node(each) node.nodes[each] = new_node @@ -70,7 +79,7 @@ def size(self): """The size method returns the number of words in the Trie.""" return self._size - def remove(self, word): +def remove(self, word): """The remove method removes the word from the Trie.""" node_list = [] node = self.root @@ -78,6 +87,8 @@ def remove(self, word): if each in node.nodes: node_list.append(node.nodes[each]) node = node.nodes[each] + else: + raise(IndexError) last = node_list.pop() if '$' not in last.nodes: return @@ -93,6 +104,7 @@ def remove(self, word): self._size -= 1 break del last.nodes[last_val] + self._size -= 1 def traversal(self, start=None): """A generator yielding values in the tree in depth first order.""" @@ -142,80 +154,3 @@ def traversal(self, start=None): if len(node.nodes) == 1 and '$' in node.nodes: break stack.append(nodes[each]) - # word = '' - # stack = [] - # branch_node = None - # to_yield = False - # stack.append(self.root) - # while len(stack) > 0: - # node = stack.pop() - # word += node.val - # if to_yield: - # yield node.val - # if start in word: - # to_yield = True - # branch_node = node - # items = node.nodes.items() - # items = reversed(items) - # nodes = OrderedDict(items) - # for each in nodes: - # if len(node.nodes) == 1 and '$' in node.nodes: - # break - # stack.append(nodes[each]) - - # stack = [] - # stack.append(self.root) - # while len(stack) > 0: - # node = stack.pop() - # if node.val != '*': - # yield node.val - # items = node.nodes.items() - # items = reversed(items) - # nodes = OrderedDict(items) - # for each in nodes: - # if len(node.nodes) == 1 and '$' in node.nodes: - # break - # stack.append(nodes[each]) - - # def _traversal(self, node): - - # stack = [] - # stack.append(node) - # while len(stack) > 0: - # node = stack.pop() - # if node.val != '*': - # yield node.val - # items = node.nodes.items() - # items = reversed(items) - # nodes = OrderedDict(items) - # for each in nodes: - # if len(node.nodes) == 1 and '$' in node.nodes: - # break - # stack.append(nodes[each]) - - - # def traversal(self, start=None): - # """The traversal method does a depth first traversal of the trie to find instances of start and return the rest.""" - # g = self._traversal(self.root) - # yield next(g) - - # def _find_start(self, start): - # """Return the next instance of start string.""" - # pass - - # def _traversal(self, node): - # """Recursive helper method for traversal. Yields value at node.""" - # print('111111111111111111111') - # import pdb; pdb.set_trace() - # if len(node.nodes) == 1 and '$' in node.nodes: - # print('2222222222222222222') - # yield node.val - # return - # yield node.val - # for each in node.nodes: - # print('333333333333333333') - # print('each', each) - # print('node.nodes[each]', node.nodes[each]) - # print(node.nodes) - # h = self._traversal(node.nodes[each]) - # yield next(h) From dcdd86ddaa74fa03e79d258444e9b6746e9eb595 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Mon, 30 Jan 2017 09:05:13 -0800 Subject: [PATCH 86/87] fixed indentation on insert and remove. --- src/trie_trav.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/trie_trav.py b/src/trie_trav.py index bb14ea4..4abf906 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -37,7 +37,7 @@ def __init__(self): self.root = Node('*') self._size = 0 -def insert(self, word): + def insert(self, word): """Insert method, which takes a word and inserts each letter of the word into the Trie, with pointer to next Node or $ if end.""" node = self.root new_node = None @@ -79,7 +79,7 @@ def size(self): """The size method returns the number of words in the Trie.""" return self._size -def remove(self, word): + def remove(self, word): """The remove method removes the word from the Trie.""" node_list = [] node = self.root From 52ab4427a8716298c361544028aa7a031da103d1 Mon Sep 17 00:00:00 2001 From: Colin Lamont Date: Tue, 31 Jan 2017 14:27:17 -0800 Subject: [PATCH 87/87] added travis. --- README.MD | 36 +++++++----------------------------- src/trie_trav.py | 3 ++- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/README.MD b/README.MD index 6871606..c57000c 100644 --- a/README.MD +++ b/README.MD @@ -1,34 +1,12 @@ +[![Build Status](https://travis-ci.org/CCallahanIV/data-structures.svg?branch=master)](https://travis-ci.org/CCallahanIV/data-structures) #Summary -The HashTable is a data structure that implements an -assosiative array. The structure maps keys to values -in such a way that values may be accessed in O(1) time. -This is done through use of a hashing function. -The hashing function maps the content of the key to -a specific bucket. As the content of the key always -maps to a specific hash value, the key's value may -be accessed quickly. - - get(key) - should return the value stored with the given key - - set(key, val) - should store the given val using the given key - - _hash(key) - should hash the key provided (note that this is an internal api) + insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. + contains(self, string): will return True if the string is in the trie, False if not. + size(self): will return the total number of words contained within the trie. 0 if empty. + remove(self, string): will remove the given string from the trie. If the word does not exist, will raise an appropriate exception. + traversal(self, start=None): a generator yielding values in the tree in depth-first order. + -# Coverage: - ------------------------------------------------------------ ----------- coverage: platform darwin, python 2.7.12-final-0 ---------- -Name Stmts Miss Cover Missing ------------------------------------------------------------ -src/hash_table.py 47 2 96% 54, 58 ------------------------------------------------------------ ----------- coverage: platform darwin, python 3.5.2-final-0 ----------- -Name Stmts Miss Cover Missing ------------------------------------------------------------ -src/hash_table.py 47 2 96% 54, 58 ------------------------------------------------------------ - -Gen Primes function used: -Sieve of Eratosthenes -Code by David Eppstein, UC Irvine, 28 Feb 2002 -http://code.activestate.com/recipes/117119/ diff --git a/src/trie_trav.py b/src/trie_trav.py index 4abf906..f51da08 100644 --- a/src/trie_trav.py +++ b/src/trie_trav.py @@ -29,7 +29,8 @@ class Trie(object): insert(self, string): will insert the input string into the trie. If character in the input string is already present, it will be ignored. contains(self, string): will return True if the string is in the trie, False if not. size(self): will return the total number of words contained within the trie. 0 if empty. - remove(self, string): will remove the given string from the trie. If the word doesn’t exist, will raise an appropriate exception. + remove(self, string): will remove the given string from the trie. If the word does not exist, will raise an appropriate exception. + traversal(self, start=None): a generator yielding values in the tree in depth-first order. """ def __init__(self):