From 90ca00acae77a0be4cfd74100225de5684a49e09 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:35:03 -0800 Subject: [PATCH 01/35] first part of BST, init class. --- bst.py | 36 ++++++++++++++++++++++++++++++++++++ test_bst.py | 0 2 files changed, 36 insertions(+) create mode 100644 bst.py create mode 100644 test_bst.py diff --git a/bst.py b/bst.py new file mode 100644 index 0000000..ebd96fb --- /dev/null +++ b/bst.py @@ -0,0 +1,36 @@ +"""Implementation of a binary search tree data structure.""" + + +class Node(object): + """Define the Node-class object.""" + + def __init__(self, value, left=None, right=None): + """Constructor for the Node class.""" + self.val = value + self.left = left + self.right = right + self.depth = 0 + + +class BST(object): + """Define the BST-class object.""" + + def __init__(self, starting_values=None): + """Constructor for the BST class.""" + self.tree_size = 0 + self.left_depth = 0 + self.right_depth = 0 + + if starting_values is None: + self.root = None + + elif isinstance(starting_values, (list, str, tuple, set)): + self.root.val = starting_values[0] + self.tree_size += 1 + for i in len(starting_values) - 1: + self.insert(starting_values[i + 1]) + + else: + raise TypeError('Only iterables or None\ + are valid parameters!') + \ No newline at end of file diff --git a/test_bst.py b/test_bst.py new file mode 100644 index 0000000..e69de29 From 4a5a47f73164c75c2c915145ddbbb19debc2c84b Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:36:57 -0800 Subject: [PATCH 02/35] changes to BST, changing depth calculation. --- bst.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/bst.py b/bst.py index ebd96fb..fbc2819 100644 --- a/bst.py +++ b/bst.py @@ -4,11 +4,12 @@ class Node(object): """Define the Node-class object.""" - def __init__(self, value, left=None, right=None): + def __init__(self, value, left=None, right=None, parent=None): """Constructor for the Node class.""" self.val = value self.left = left self.right = right + self.parent = parent self.depth = 0 @@ -20,17 +21,21 @@ def __init__(self, starting_values=None): self.tree_size = 0 self.left_depth = 0 self.right_depth = 0 + self.visited = [] if starting_values is None: self.root = None - elif isinstance(starting_values, (list, str, tuple, set)): - self.root.val = starting_values[0] + elif isinstance(starting_values, (list, str, tuple)): + self.root = Node(starting_values[0]) self.tree_size += 1 - for i in len(starting_values) - 1: + for i in range(len(starting_values) - 1): self.insert(starting_values[i + 1]) else: raise TypeError('Only iterables or None\ are valid parameters!') - \ No newline at end of file + + def balance(self): + """Return the current balance of the BST.""" + return self.right_depth - self.left_depth \ No newline at end of file From 832b1092d564414df18434b409118cc5d4053ce7 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:38:26 -0800 Subject: [PATCH 03/35] changes to bst, first traversal methods. --- bst.py | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/bst.py b/bst.py index fbc2819..f2a3960 100644 --- a/bst.py +++ b/bst.py @@ -38,4 +38,140 @@ def __init__(self, starting_values=None): def balance(self): """Return the current balance of the BST.""" - return self.right_depth - self.left_depth \ No newline at end of file + return self.right_depth - self.left_depth + + def size(self): + """Return the current size of the BST.""" + return self.tree_size + + def insert(self, value): + """Insert a new node into the BST, and adjust the balance.""" + new_node = Node(value) + + if self.root: + if new_node.val > self.root.val: + if self.root.right: + self._find_home(new_node, self.root.right) + if new_node.depth > self.right_depth: + self.right_depth = new_node.depth + else: + new_node.parent = self.root + self.root.right = new_node + self.root.right.depth = 1 + if self.root.right.depth > self.right_depth: + self.right_depth = self.root.right.depth + self.tree_size += 1 + + elif new_node.val < self.root.val: + if self.root.left: + self._find_home(new_node, self.root.left) + if new_node.depth > self.left_depth: + self.left_depth = new_node.depth + else: + new_node.parent = self.root + self.root.left = new_node + self.root.left.depth = 1 + if self.root.left.depth > self.left_depth: + self.left_depth = self.root.left.depth + self.tree_size += 1 + else: + self.root = new_node + self.tree_size += 1 + + def _find_home(self, node_to_add, node_to_check): + """. + Check if the node_to_add belongs on the left or right + of the node_to_check, then place it there if that spot is empty, + otherwise recur. + """ + if node_to_add.val > node_to_check.val: + if node_to_check.right: + self._find_home(node_to_add, node_to_check.right) + else: + node_to_add.parent = node_to_check + node_to_check.right = node_to_add + node_to_check.right.depth = node_to_check.depth + 1 + self.tree_size += 1 + + elif node_to_add.val < node_to_check.val: + if node_to_check.left: + self._find_home(node_to_add, node_to_check.left) + else: + node_to_add.parent = node_to_check + node_to_check.left = node_to_add + node_to_check.left.depth = node_to_check.depth + 1 + self.tree_size += 1 + + def search(self, value): + """If a value is in the BST, return its node.""" + return self._check_for_equivalence(value, self.root) + + def contains(self, value): + """Return whether or not a value is in the BST.""" + return bool(self.search(value)) + + def _check_for_equivalence(self, value, node_to_check): + """. + Check if the value matches that of the node_to_check + if it does, return the node. If it doesn't, go left or right + as appropriate and recur. If you reach a dead end, return None. + """ + try: + if value == node_to_check.val: + return node_to_check + + except AttributeError: + return None + + if value > node_to_check.val and node_to_check.right: + return self._check_for_equivalence(value, node_to_check.right) + + elif value < node_to_check.val and node_to_check.left: + return self._check_for_equivalence(value, node_to_check.left) + + def depth(self): + """Return the depth of the BST.""" + if self.left_depth > self.right_depth: + return self.left_depth + return self.right_depth + + def in_order(self): + """Return a generator to perform an in-order traversal.""" + self.visited = [] + + if self.root is None: + raise IndexError("Tree is empty!") + + gen = self._in_order_gen() + return gen + + def _in_order_gen(self): + """Recursive helper method for in-order traversal.""" + current = self.root + + while len(self.visited) < self.tree_size: + if current.left: + if current.left.val not in self.visited: + current = current.left + continue + + if current.val not in self.visited: + self.visited.append(current.val) + yield current.val + + if current.right: + if current.right.val not in self.visited: + current = current.right + continue + + current = current.parent + + def pre_order(self): + """Return a generator to perform an pre-order traversal.""" + self.visited = [] + + if self.root is None: + raise IndexError("Tree is empty!") + + gen = self._pre_order_gen() + return gen From 0b26b1f4d13ea5430f27e90b1cb0d5645905f11e Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:38:46 -0800 Subject: [PATCH 04/35] preorder gen traversal. --- bst.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/bst.py b/bst.py index f2a3960..4f50c8f 100644 --- a/bst.py +++ b/bst.py @@ -175,3 +175,24 @@ def pre_order(self): gen = self._pre_order_gen() return gen + + def _pre_order_gen(self): + """Recursive helper method for pre-order traversal.""" + current = self.root + + while len(self.visited) < self.tree_size: + if current.val not in self.visited: + self.visited.append(current.val) + yield current.val + + if current.left: + if current.left.val not in self.visited: + current = current.left + continue + + if current.right: + if current.right.val not in self.visited: + current = current.right + continue + + current = current.parent From 498a807ddffeb2b87a5c97b28ded1d692c2ff09e Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:39:04 -0800 Subject: [PATCH 05/35] post_order --- bst.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bst.py b/bst.py index 4f50c8f..d1593dd 100644 --- a/bst.py +++ b/bst.py @@ -196,3 +196,13 @@ def _pre_order_gen(self): continue current = current.parent + + def post_order(self): + """Return a generator to perform an post-order traversal.""" + self.visited = [] + + if self.root is None: + raise IndexError("Tree is empty!") + + gen = self._post_order_gen() + return gen From 1c0b988fc8623e106915c84036fc3157975fc6ea Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:39:28 -0800 Subject: [PATCH 06/35] bredth_first --- bst.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/bst.py b/bst.py index d1593dd..1f2df2f 100644 --- a/bst.py +++ b/bst.py @@ -206,3 +206,34 @@ def post_order(self): gen = self._post_order_gen() return gen + + def _post_order_gen(self): + """Recursive helper method for post-order traversal.""" + current = self.root + + while len(self.visited) < self.tree_size: + if current.left: + if current.left.val not in self.visited: + current = current.left + continue + + if current.right: + if current.right.val not in self.visited: + current = current.right + continue + + if current.val not in self.visited: + self.visited.append(current.val) + yield current.val + + current = current.parent + + def breadth_first(self): + """Return a generator to perform a breadth-first traversal.""" + self.visited = [] + + if self.root is None: + raise IndexError("Tree is empty!") + + gen = self._breadth_first_gen(self.root) + return gen From 806f991cc8f7b0350913ea019da6ea50da30f828 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:39:56 -0800 Subject: [PATCH 07/35] finishing breadth first and post order gen. --- bst.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bst.py b/bst.py index 1f2df2f..96faef5 100644 --- a/bst.py +++ b/bst.py @@ -237,3 +237,22 @@ def breadth_first(self): gen = self._breadth_first_gen(self.root) return gen + + def _breadth_first_gen(self, root_node): + """Helper generator for breadth-first traversal.""" + queue = [self.root] + while queue: + current = queue[0] + yield current.val + queue = queue[1:] + + if current not in self.visited: + self.visited.append(current) + + if current.left: + if current.left not in self.visited: + queue.append(current.left) + + if current.right: + if current.right not in self.visited: + queue.append(current.right) From 116a52f9f73b787cdb07b69ca6065beaafd96ac1 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:45:20 -0800 Subject: [PATCH 08/35] adding tests --- bst.py | 1 + test_bst.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/bst.py b/bst.py index 96faef5..ed26a2e 100644 --- a/bst.py +++ b/bst.py @@ -78,6 +78,7 @@ def insert(self, value): self.root = new_node self.tree_size += 1 + def _find_home(self, node_to_add, node_to_check): """. Check if the node_to_add belongs on the left or right diff --git a/test_bst.py b/test_bst.py index e69de29..4639fa7 100644 --- a/test_bst.py +++ b/test_bst.py @@ -0,0 +1,136 @@ +"""Various tests for the Binary Search Tree.""" + +import pytest + + +@pytest.fixture +def sample_bst(): + """Make a sample_bst for testing.""" + from bst import BST + return BST() + + +def test_bst_exists(sample_bst): + """Test that the BST class makes something.""" + assert sample_bst + + +def test_bst_can_take_list(): + """Test that the BST can take a list as a param.""" + from bst import BST + b = BST([1, 2, 3]) + assert b.size() == 3 + assert b.depth() == 2 + assert b.left_depth == 0 + assert b.right_depth == 2 + + +def test_bst_can_take_tuple(): + """Test that the BST can take a tuple as a param.""" + from bst import BST + b = BST((1, 2, 3)) + assert b.size() == 3 + assert b.depth() == 2 + assert b.left_depth == 0 + assert b.right_depth == 2 + + +def test_bst_can_take_string(): + """Test that the BST can take a string as a param.""" + from bst import BST + b = BST('abc') + assert b.size() == 3 + assert b.depth() == 2 + assert b.left_depth == 0 + assert b.right_depth == 2 + + +def test_insert_increases_size(sample_bst): + """Test that the insert method increases the output of the size method.""" + assert sample_bst.size() == 0 + sample_bst.insert(1) + assert sample_bst.size() == 1 + sample_bst.insert(2) + assert sample_bst.size() == 2 + + +def test_insert_increases_tree_size(sample_bst): + """Test that the insert method increases the tree_size attribute.""" + assert sample_bst.tree_size == 0 + sample_bst.insert(1) + assert sample_bst.tree_size == 1 + sample_bst.insert(2) + assert sample_bst.tree_size == 2 + + +def test_insert_increases_depth(sample_bst): + """Test that the insert method increases the output of the depth method.""" + assert sample_bst.depth() == 0 + sample_bst.insert(1) + assert sample_bst.depth() == 0 + sample_bst.insert(2) + assert sample_bst.depth() == 1 + + +def test_search_right(sample_bst): + """Assert that the search method returns something and that it's a Node.""" + from bst import Node + sample_bst.insert(1) + sample_bst.insert(2) + sample_bst.insert(3) + found = sample_bst.search(3) + assert found.val == 3 + assert isinstance(found, Node) + + +def test_search_left(sample_bst): + """Assert that the search method returns something and that it's a Node.""" + from bst import Node + sample_bst.insert(3) + sample_bst.insert(2) + sample_bst.insert(1) + found = sample_bst.search(3) + assert found.val == 3 + assert isinstance(found, Node) + + +def test_search_not_found_returns_none(sample_bst): + """Assert that the search method returns None when value isn't found.""" + sample_bst.insert(1) + sample_bst.insert(2) + sample_bst.insert(3) + found = sample_bst.search(4) + assert found is None + + +def test_contains_right(sample_bst): + """Assert that the contains method returns True.""" + sample_bst.insert(1) + sample_bst.insert(2) + sample_bst.insert(3) + found = sample_bst.contains(3) + assert found is True + + +def test_contains_left(sample_bst): + """Assert that the contains method returns True.""" + sample_bst.insert(3) + sample_bst.insert(2) + sample_bst.insert(1) + found = sample_bst.contains(3) + assert found is True + + +def test_contains_not_found_returns_none(sample_bst): + """Assert that the contains method returns False when value isn't found.""" + sample_bst.insert(1) + sample_bst.insert(2) + sample_bst.insert(3) + found = sample_bst.contains(4) + assert found is False + + +def test_in_order_errors_with_empty_tree(sample_bst): + """Test that in_order raises an IndexError if the tree is empty.""" + with pytest.raises(IndexError): + assert found is False From 7c7b83e67994a48499d0358f7bbdce622cbf9599 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 12:59:54 -0800 Subject: [PATCH 09/35] adding 8 new tests for BST edge cases. --- test_bst.py | 270 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 165 insertions(+), 105 deletions(-) diff --git a/test_bst.py b/test_bst.py index 4639fa7..571b834 100644 --- a/test_bst.py +++ b/test_bst.py @@ -1,6 +1,9 @@ """Various tests for the Binary Search Tree.""" import pytest +from bst import BST +from bst import Node + @pytest.fixture @@ -15,122 +18,179 @@ def test_bst_exists(sample_bst): assert sample_bst -def test_bst_can_take_list(): - """Test that the BST can take a list as a param.""" - from bst import BST - b = BST([1, 2, 3]) - assert b.size() == 3 - assert b.depth() == 2 - assert b.left_depth == 0 - assert b.right_depth == 2 - - -def test_bst_can_take_tuple(): - """Test that the BST can take a tuple as a param.""" - from bst import BST - b = BST((1, 2, 3)) - assert b.size() == 3 - assert b.depth() == 2 - assert b.left_depth == 0 - assert b.right_depth == 2 - - -def test_bst_can_take_string(): - """Test that the BST can take a string as a param.""" - from bst import BST - b = BST('abc') - assert b.size() == 3 - assert b.depth() == 2 - assert b.left_depth == 0 - assert b.right_depth == 2 - - -def test_insert_increases_size(sample_bst): - """Test that the insert method increases the output of the size method.""" +# def test_bst_can_take_list(): +# """Test that the BST can take a list as a param.""" +# from bst import BST +# b = BST([1, 2, 3]) +# assert b.size() == 3 +# assert b.depth() == 2 +# assert b.left_depth == 0 +# assert b.right_depth == 2 + + +# def test_bst_can_take_tuple(): +# """Test that the BST can take a tuple as a param.""" +# from bst import BST +# b = BST((1, 2, 3)) +# assert b.size() == 3 +# assert b.depth() == 2 +# assert b.left_depth == 0 +# assert b.right_depth == 2 + + +# def test_bst_can_take_string(): +# """Test that the BST can take a string as a param.""" +# from bst import BST +# b = BST('abc') +# assert b.size() == 3 +# assert b.depth() == 2 +# assert b.left_depth == 0 +# assert b.right_depth == 2 + + +# def test_insert_increases_size(sample_bst): +# """Test that the insert method increases the output of the size method.""" +# assert sample_bst.size() == 0 +# sample_bst.insert(1) +# assert sample_bst.size() == 1 +# sample_bst.insert(2) +# assert sample_bst.size() == 2 + + +# def test_insert_increases_tree_size(sample_bst): +# """Test that the insert method increases the tree_size attribute.""" +# assert sample_bst.tree_size == 0 +# sample_bst.insert(1) +# assert sample_bst.tree_size == 1 +# sample_bst.insert(2) +# assert sample_bst.tree_size == 2 + + +# def test_insert_increases_depth(sample_bst): +# """Test that the insert method increases the output of the depth method.""" +# assert sample_bst.depth() == 0 +# sample_bst.insert(1) +# assert sample_bst.depth() == 0 +# sample_bst.insert(2) +# assert sample_bst.depth() == 1 + + +# def test_search_right(sample_bst): +# """Assert that the search method returns something and that it's a Node.""" +# from bst import Node +# sample_bst.insert(1) +# sample_bst.insert(2) +# sample_bst.insert(3) +# found = sample_bst.search(3) +# assert found.val == 3 +# assert isinstance(found, Node) + + +# def test_search_left(sample_bst): +# """Assert that the search method returns something and that it's a Node.""" +# from bst import Node +# sample_bst.insert(3) +# sample_bst.insert(2) +# sample_bst.insert(1) +# found = sample_bst.search(3) +# assert found.val == 3 +# assert isinstance(found, Node) + + +# def test_search_not_found_returns_none(sample_bst): +# """Assert that the search method returns None when value isn't found.""" +# sample_bst.insert(1) +# sample_bst.insert(2) +# sample_bst.insert(3) +# found = sample_bst.search(4) +# assert found is None + + +# def test_contains_right(sample_bst): +# """Assert that the contains method returns True.""" +# sample_bst.insert(1) +# sample_bst.insert(2) +# sample_bst.insert(3) +# found = sample_bst.contains(3) +# assert found is True + + +# def test_contains_left(sample_bst): +# """Assert that the contains method returns True.""" +# sample_bst.insert(3) +# sample_bst.insert(2) +# sample_bst.insert(1) +# found = sample_bst.contains(3) +# assert found is True + + +# def test_contains_not_found_returns_none(sample_bst): +# """Assert that the contains method returns False when value isn't found.""" +# sample_bst.insert(1) +# sample_bst.insert(2) +# sample_bst.insert(3) +# found = sample_bst.contains(4) +# assert found is False + + +# def test_in_order_errors_with_empty_tree(sample_bst): +# """Test that in_order raises an IndexError if the tree is empty.""" +# with pytest.raises(IndexError): +# assert found is False + +# Chelsea tests below + +def test_that_bst_doesnt_work_with_non_iterable(): + """Test that BST only takes iterable inputs.""" + with pytest.raises(TypeError): + BST({0: 0, 1: 1, 2: 2}) + + +def test_adding_preexisting_node_is_not_added(sample_bst): + """Test that adding a node val that exists does not increase BST.""" assert sample_bst.size() == 0 - sample_bst.insert(1) + sample_bst.insert(5) + sample_bst.insert(5) + sample_bst.insert(5) assert sample_bst.size() == 1 - sample_bst.insert(2) - assert sample_bst.size() == 2 - - -def test_insert_increases_tree_size(sample_bst): - """Test that the insert method increases the tree_size attribute.""" - assert sample_bst.tree_size == 0 - sample_bst.insert(1) - assert sample_bst.tree_size == 1 - sample_bst.insert(2) - assert sample_bst.tree_size == 2 -def test_insert_increases_depth(sample_bst): - """Test that the insert method increases the output of the depth method.""" - assert sample_bst.depth() == 0 - sample_bst.insert(1) - assert sample_bst.depth() == 0 - sample_bst.insert(2) - assert sample_bst.depth() == 1 - - -def test_search_right(sample_bst): - """Assert that the search method returns something and that it's a Node.""" - from bst import Node - sample_bst.insert(1) - sample_bst.insert(2) - sample_bst.insert(3) - found = sample_bst.search(3) - assert found.val == 3 - assert isinstance(found, Node) - - -def test_search_left(sample_bst): - """Assert that the search method returns something and that it's a Node.""" - from bst import Node - sample_bst.insert(3) - sample_bst.insert(2) - sample_bst.insert(1) - found = sample_bst.search(3) - assert found.val == 3 - assert isinstance(found, Node) +def test_that_negative_numbers_work_with_insert(sample_bst): + """Test that negative numbers are covered in insert.""" + sample_bst.insert(-500) + assert sample_bst -def test_search_not_found_returns_none(sample_bst): - """Assert that the search method returns None when value isn't found.""" - sample_bst.insert(1) - sample_bst.insert(2) - sample_bst.insert(3) - found = sample_bst.search(4) - assert found is None +def test_node_attributes_exist(): + """Test that node attributes are in Node class.""" + n = Node(1) + assert n.val == 1 + assert n.left is None + assert n.right is None + assert n.depth == 0 -def test_contains_right(sample_bst): - """Assert that the contains method returns True.""" - sample_bst.insert(1) - sample_bst.insert(2) +def test_node_attribute_depth_changes(sample_bst): + """Test that node attribute depth increases.""" + sample_bst.insert(4) sample_bst.insert(3) - found = sample_bst.contains(3) - assert found is True + sample_bst.insert(2.5) + assert sample_bst.search(4).depth == 0 + assert sample_bst.search(3).depth == 1 + assert sample_bst.search(2.5).depth == 2 -def test_contains_left(sample_bst): - """Assert that the contains method returns True.""" - sample_bst.insert(3) - sample_bst.insert(2) - sample_bst.insert(1) - found = sample_bst.contains(3) - assert found is True +def test_node_left_and_right_attributes_change(): + """Test that left and right node attributes are added with insert.""" + b = BST([5]) + b.insert(4) + b.insert(6) + assert b.root.left.val == 4 + assert b.root.right.val == 6 - -def test_contains_not_found_returns_none(sample_bst): - """Assert that the contains method returns False when value isn't found.""" - sample_bst.insert(1) - sample_bst.insert(2) - sample_bst.insert(3) - found = sample_bst.contains(4) - assert found is False +# NOTE: THERE WILL BE PROBS WITH DOT NOTATION IN PREVIOUS TWO TESTS -def test_in_order_errors_with_empty_tree(sample_bst): - """Test that in_order raises an IndexError if the tree is empty.""" - with pytest.raises(IndexError): - assert found is False +def test_root_val_with_no_val_at_initialization(sample_bst): + """Test that root is None.""" + assert sample_bst.root is None From 5e219b1f6d06e5fd43ae8521c2920e45c46875d3 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 13:01:15 -0800 Subject: [PATCH 10/35] fixing small error in BST tests. --- test_bst.py | 234 +++++++++++++++++++++++++--------------------------- 1 file changed, 114 insertions(+), 120 deletions(-) diff --git a/test_bst.py b/test_bst.py index 571b834..f1b686d 100644 --- a/test_bst.py +++ b/test_bst.py @@ -5,7 +5,6 @@ from bst import Node - @pytest.fixture def sample_bst(): """Make a sample_bst for testing.""" @@ -18,128 +17,123 @@ def test_bst_exists(sample_bst): assert sample_bst -# def test_bst_can_take_list(): -# """Test that the BST can take a list as a param.""" -# from bst import BST -# b = BST([1, 2, 3]) -# assert b.size() == 3 -# assert b.depth() == 2 -# assert b.left_depth == 0 -# assert b.right_depth == 2 - - -# def test_bst_can_take_tuple(): -# """Test that the BST can take a tuple as a param.""" -# from bst import BST -# b = BST((1, 2, 3)) -# assert b.size() == 3 -# assert b.depth() == 2 -# assert b.left_depth == 0 -# assert b.right_depth == 2 - - -# def test_bst_can_take_string(): -# """Test that the BST can take a string as a param.""" -# from bst import BST -# b = BST('abc') -# assert b.size() == 3 -# assert b.depth() == 2 -# assert b.left_depth == 0 -# assert b.right_depth == 2 - - -# def test_insert_increases_size(sample_bst): -# """Test that the insert method increases the output of the size method.""" -# assert sample_bst.size() == 0 -# sample_bst.insert(1) -# assert sample_bst.size() == 1 -# sample_bst.insert(2) -# assert sample_bst.size() == 2 - - -# def test_insert_increases_tree_size(sample_bst): -# """Test that the insert method increases the tree_size attribute.""" -# assert sample_bst.tree_size == 0 -# sample_bst.insert(1) -# assert sample_bst.tree_size == 1 -# sample_bst.insert(2) -# assert sample_bst.tree_size == 2 - - -# def test_insert_increases_depth(sample_bst): -# """Test that the insert method increases the output of the depth method.""" -# assert sample_bst.depth() == 0 -# sample_bst.insert(1) -# assert sample_bst.depth() == 0 -# sample_bst.insert(2) -# assert sample_bst.depth() == 1 - - -# def test_search_right(sample_bst): -# """Assert that the search method returns something and that it's a Node.""" -# from bst import Node -# sample_bst.insert(1) -# sample_bst.insert(2) -# sample_bst.insert(3) -# found = sample_bst.search(3) -# assert found.val == 3 -# assert isinstance(found, Node) - - -# def test_search_left(sample_bst): -# """Assert that the search method returns something and that it's a Node.""" -# from bst import Node -# sample_bst.insert(3) -# sample_bst.insert(2) -# sample_bst.insert(1) -# found = sample_bst.search(3) -# assert found.val == 3 -# assert isinstance(found, Node) - - -# def test_search_not_found_returns_none(sample_bst): -# """Assert that the search method returns None when value isn't found.""" -# sample_bst.insert(1) -# sample_bst.insert(2) -# sample_bst.insert(3) -# found = sample_bst.search(4) -# assert found is None - - -# def test_contains_right(sample_bst): -# """Assert that the contains method returns True.""" -# sample_bst.insert(1) -# sample_bst.insert(2) -# sample_bst.insert(3) -# found = sample_bst.contains(3) -# assert found is True - - -# def test_contains_left(sample_bst): -# """Assert that the contains method returns True.""" -# sample_bst.insert(3) -# sample_bst.insert(2) -# sample_bst.insert(1) -# found = sample_bst.contains(3) -# assert found is True - - -# def test_contains_not_found_returns_none(sample_bst): -# """Assert that the contains method returns False when value isn't found.""" -# sample_bst.insert(1) -# sample_bst.insert(2) -# sample_bst.insert(3) -# found = sample_bst.contains(4) -# assert found is False - - -# def test_in_order_errors_with_empty_tree(sample_bst): -# """Test that in_order raises an IndexError if the tree is empty.""" -# with pytest.raises(IndexError): -# assert found is False +def test_bst_can_take_list(): + """Test that the BST can take a list as a param.""" + from bst import BST + b = BST([1, 2, 3]) + assert b.size() == 3 + assert b.depth() == 2 + assert b.left_depth == 0 + assert b.right_depth == 2 + + +def test_bst_can_take_tuple(): + """Test that the BST can take a tuple as a param.""" + from bst import BST + b = BST((1, 2, 3)) + assert b.size() == 3 + assert b.depth() == 2 + assert b.left_depth == 0 + assert b.right_depth == 2 + + +def test_bst_can_take_string(): + """Test that the BST can take a string as a param.""" + from bst import BST + b = BST('abc') + assert b.size() == 3 + assert b.depth() == 2 + assert b.left_depth == 0 + assert b.right_depth == 2 + + +def test_insert_increases_size(sample_bst): + """Test that the insert method increases the output of the size method.""" + assert sample_bst.size() == 0 + sample_bst.insert(1) + assert sample_bst.size() == 1 + sample_bst.insert(2) + assert sample_bst.size() == 2 + + +def test_insert_increases_tree_size(sample_bst): + """Test that the insert method increases the tree_size attribute.""" + assert sample_bst.tree_size == 0 + sample_bst.insert(1) + assert sample_bst.tree_size == 1 + sample_bst.insert(2) + assert sample_bst.tree_size == 2 + + +def test_insert_increases_depth(sample_bst): + """Test that the insert method increases the output of the depth method.""" + assert sample_bst.depth() == 0 + sample_bst.insert(1) + assert sample_bst.depth() == 0 + sample_bst.insert(2) + assert sample_bst.depth() == 1 + + +def test_search_right(sample_bst): + """Assert that the search method returns something and that it's a Node.""" + from bst import Node + sample_bst.insert(1) + sample_bst.insert(2) + sample_bst.insert(3) + found = sample_bst.search(3) + assert found.val == 3 + assert isinstance(found, Node) + + +def test_search_left(sample_bst): + """Assert that the search method returns something and that it's a Node.""" + from bst import Node + sample_bst.insert(3) + sample_bst.insert(2) + sample_bst.insert(1) + found = sample_bst.search(3) + assert found.val == 3 + assert isinstance(found, Node) + + +def test_search_not_found_returns_none(sample_bst): + """Assert that the search method returns None when value isn't found.""" + sample_bst.insert(1) + sample_bst.insert(2) + sample_bst.insert(3) + found = sample_bst.search(4) + assert found is None + + +def test_contains_right(sample_bst): + """Assert that the contains method returns True.""" + sample_bst.insert(1) + sample_bst.insert(2) + sample_bst.insert(3) + found = sample_bst.contains(3) + assert found is True + + +def test_contains_left(sample_bst): + """Assert that the contains method returns True.""" + sample_bst.insert(3) + sample_bst.insert(2) + sample_bst.insert(1) + found = sample_bst.contains(3) + assert found is True + + +def test_contains_not_found_returns_none(sample_bst): + """Assert that the contains method returns False when value isn't found.""" + sample_bst.insert(1) + sample_bst.insert(2) + sample_bst.insert(3) + found = sample_bst.contains(4) + assert found is False # Chelsea tests below + def test_that_bst_doesnt_work_with_non_iterable(): """Test that BST only takes iterable inputs.""" with pytest.raises(TypeError): From 1cd5bc0e7c63fd1ece7515c465c706e3e35e6e37 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Fri, 24 Nov 2017 13:18:47 -0800 Subject: [PATCH 11/35] last changes --- bst.py | 1 - test_bst.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/bst.py b/bst.py index ed26a2e..96faef5 100644 --- a/bst.py +++ b/bst.py @@ -78,7 +78,6 @@ def insert(self, value): self.root = new_node self.tree_size += 1 - def _find_home(self, node_to_add, node_to_check): """. Check if the node_to_add belongs on the left or right diff --git a/test_bst.py b/test_bst.py index f1b686d..9efdb67 100644 --- a/test_bst.py +++ b/test_bst.py @@ -182,8 +182,6 @@ def test_node_left_and_right_attributes_change(): assert b.root.left.val == 4 assert b.root.right.val == 6 -# NOTE: THERE WILL BE PROBS WITH DOT NOTATION IN PREVIOUS TWO TESTS - def test_root_val_with_no_val_at_initialization(sample_bst): """Test that root is None.""" From bd0e281611ff5593067c7486efdf467534cedb18 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 22:35:51 -0800 Subject: [PATCH 12/35] adding tests Nathan and I wrote on Friday. --- test_bst.py | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 1 deletion(-) diff --git a/test_bst.py b/test_bst.py index 9efdb67..2b75c5b 100644 --- a/test_bst.py +++ b/test_bst.py @@ -1,4 +1,4 @@ -"""Various tests for the Binary Search Tree.""" +"""Tests for the Binary Search Tree.""" import pytest from bst import BST @@ -186,3 +186,182 @@ def test_node_left_and_right_attributes_change(): def test_root_val_with_no_val_at_initialization(sample_bst): """Test that root is None.""" assert sample_bst.root is None + + + +def test_in_order_errors_with_empty_tree(sample_bst): + """Test that in_order raises an IndexError if the tree is empty.""" + with pytest.raises(IndexError): + sample_bst.in_order() + + +def test_pre_order_errors_with_empty_tree(sample_bst): + """Test that pre_order raises an IndexError if the tree is empty.""" + with pytest.raises(IndexError): + sample_bst.pre_order() + + +def test_post_order_errors_with_empty_tree(sample_bst): + """Test that post_order raises an IndexError if the tree is empty.""" + with pytest.raises(IndexError): + sample_bst.post_order() + + +def test_breadth_first_errors_with_empty_tree(sample_bst): + """Test that breadth_first raises an IndexError if the tree is empty.""" + with pytest.raises(IndexError): + sample_bst.breadth_first() + + +def test_in_order_size_one(sample_bst): + """Check for the correct output of in_order on a tree of size 1.""" + sample_bst.insert(1) + gen = sample_bst.in_order() + assert next(gen) == 1 + + +def test_pre_order_size_one(sample_bst): + """Check for the correct output of pre_order on a tree of size 1.""" + sample_bst.insert(1) + gen = sample_bst.pre_order() + assert next(gen) == 1 + + +def test_post_order_size_one(sample_bst): + """Check for the correct output of post_order on a tree of size 1.""" + sample_bst.insert(1) + gen = sample_bst.post_order() + assert next(gen) == 1 + + +def test_breadth_first_size_one(sample_bst): + """Check for the correct output of breadth_first on a tree of size 1.""" + sample_bst.insert(1) + gen = sample_bst.breadth_first() + assert next(gen) == 1 + +LEFT_IMBALANCED = [6, 5, 4, 3, 2, 1] + + +def test_in_order_left_imba(): + """Check for the correct output of iot on a left-imbalanced tree.""" + tree = BST(LEFT_IMBALANCED) + gen = tree.in_order() + output = [] + for i in range(6): + output.append(next(gen)) + assert output == [1, 2, 3, 4, 5, 6] + + +def test_pre_order_left_imba(): + """Check for the correct output of preo-t on a left-imbalanced tree.""" + tree = BST(LEFT_IMBALANCED) + gen = tree.pre_order() + output = [] + for i in range(6): + output.append(next(gen)) + assert output == [6, 5, 4, 3, 2, 1] + + +def test_post_order_left_imba(): + """Check for the correct output of posto-t on a left-imbalanced tree.""" + tree = BST(LEFT_IMBALANCED) + gen = tree.post_order() + output = [] + for i in range(6): + output.append(next(gen)) + assert output == [1, 2, 3, 4, 5, 6] + + +def test_breadth_first_left_imba(): + """Check for the correct output of bft on a left-imbalanced tree.""" + tree = BST(LEFT_IMBALANCED) + gen = tree.breadth_first() + output = [] + for i in range(6): + output.append(next(gen)) + assert output == [6, 5, 4, 3, 2, 1] + +RIGHT_IMBALANCED = [1, 2, 3, 4, 5, 6] + + +def test_in_order_right_imba(): + """Check for the correct output of iot on a right-imbalanced tree.""" + tree = BST(RIGHT_IMBALANCED) + gen = tree.in_order() + output = [] + for i in range(6): + output.append(next(gen)) + assert output == [1, 2, 3, 4, 5, 6] + + +def test_pre_order_right_imba(): + """Check for the correct output of preo-t on a right-imbalanced tree.""" + tree = BST(RIGHT_IMBALANCED) + gen = tree.pre_order() + output = [] + for i in range(6): + output.append(next(gen)) + assert output == [1, 2, 3, 4, 5, 6] + + +def test_post_order_right_imba(): + """Check for the correct output of posto-t on a right-imbalanced tree.""" + tree = BST(RIGHT_IMBALANCED) + gen = tree.post_order() + output = [] + for i in range(6): + output.append(next(gen)) + assert output == [6, 5, 4, 3, 2, 1] + + +def test_breadth_first_right_imba(): + """Check for the correct output of bft on a right-imbalanced tree.""" + tree = BST(RIGHT_IMBALANCED) + gen = tree.breadth_first() + output = [] + for i in range(6): + output.append(next(gen)) + assert output == [1, 2, 3, 4, 5, 6] + +SAMPLE_TREE = [20, 12, 10, 1, 11, 16, 30, 42, 28, 27] + + +def test_in_order_sample_tree(): + """Check for the correct output of iot on a sample tree.""" + tree = BST(SAMPLE_TREE) + gen = tree.in_order() + output = [] + for i in range(10): + output.append(next(gen)) + assert output == [1, 10, 11, 12, 16, 20, 27, 28, 30, 42] + + +def test_pre_order_sample_tree(): + """Check for the correct output of preo-t on a sample tree.""" + tree = BST(SAMPLE_TREE) + gen = tree.pre_order() + output = [] + for i in range(10): + output.append(next(gen)) + assert output == [20, 12, 10, 1, 11, 16, 30, 28, 27, 42] + + +def test_post_order_sample_tree(): + """Check for the correct output of posto-t on a sample tree.""" + tree = BST(SAMPLE_TREE) + gen = tree.post_order() + output = [] + for i in range(10): + output.append(next(gen)) + assert output == [1, 11, 10, 16, 12, 27, 28, 42, 30, 20] + + +def test_breadth_first_sample_tree(): + """Check for the correct output of bft on a right-imbalanced tree.""" + tree = BST(SAMPLE_TREE) + gen = tree.breadth_first() + output = [] + for i in range(10): + output.append(next(gen)) + assert output == [20, 12, 30, 10, 16, 28, 42, 1, 11, 27] From 06f48b7b9ba7562d368e0c9d0ffaa9530d48b875 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 22:37:16 -0800 Subject: [PATCH 13/35] small changes to naming of tests and docstrings to be more semantic. --- test_bst.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test_bst.py b/test_bst.py index 2b75c5b..b811274 100644 --- a/test_bst.py +++ b/test_bst.py @@ -131,8 +131,6 @@ def test_contains_not_found_returns_none(sample_bst): found = sample_bst.contains(4) assert found is False -# Chelsea tests below - def test_that_bst_doesnt_work_with_non_iterable(): """Test that BST only takes iterable inputs.""" @@ -188,26 +186,25 @@ def test_root_val_with_no_val_at_initialization(sample_bst): assert sample_bst.root is None - -def test_in_order_errors_with_empty_tree(sample_bst): +def test_in_order_indexerrors_with_empty_tree(sample_bst): """Test that in_order raises an IndexError if the tree is empty.""" with pytest.raises(IndexError): sample_bst.in_order() -def test_pre_order_errors_with_empty_tree(sample_bst): +def test_pre_order_indexerrors_with_empty_tree(sample_bst): """Test that pre_order raises an IndexError if the tree is empty.""" with pytest.raises(IndexError): sample_bst.pre_order() -def test_post_order_errors_with_empty_tree(sample_bst): +def test_post_order_indexerrors_with_empty_tree(sample_bst): """Test that post_order raises an IndexError if the tree is empty.""" with pytest.raises(IndexError): sample_bst.post_order() -def test_breadth_first_errors_with_empty_tree(sample_bst): +def test_breadth_first_indexerrors_with_empty_tree(sample_bst): """Test that breadth_first raises an IndexError if the tree is empty.""" with pytest.raises(IndexError): sample_bst.breadth_first() From d46e7bec9a567d0520cea8148f1e80fcd44e1d8d Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 22:41:04 -0800 Subject: [PATCH 14/35] moving and changing sample trees --- test_bst.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test_bst.py b/test_bst.py index b811274..54fd8c2 100644 --- a/test_bst.py +++ b/test_bst.py @@ -237,7 +237,10 @@ def test_breadth_first_size_one(sample_bst): gen = sample_bst.breadth_first() assert next(gen) == 1 + LEFT_IMBALANCED = [6, 5, 4, 3, 2, 1] +RIGHT_IMBALANCED = [1, 2, 3, 4, 5, 6] +SAMPLE_TREE = [20, 12, 10, 1, 11, 16, 30, 42, 28, 27] def test_in_order_left_imba(): @@ -279,8 +282,6 @@ def test_breadth_first_left_imba(): output.append(next(gen)) assert output == [6, 5, 4, 3, 2, 1] -RIGHT_IMBALANCED = [1, 2, 3, 4, 5, 6] - def test_in_order_right_imba(): """Check for the correct output of iot on a right-imbalanced tree.""" @@ -321,8 +322,6 @@ def test_breadth_first_right_imba(): output.append(next(gen)) assert output == [1, 2, 3, 4, 5, 6] -SAMPLE_TREE = [20, 12, 10, 1, 11, 16, 30, 42, 28, 27] - def test_in_order_sample_tree(): """Check for the correct output of iot on a sample tree.""" From 61847674dd97c12cd182f50506adc63387a0e868 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 22:42:31 -0800 Subject: [PATCH 15/35] last commit, further semantic changes --- test_bst.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test_bst.py b/test_bst.py index 54fd8c2..77b764f 100644 --- a/test_bst.py +++ b/test_bst.py @@ -17,8 +17,8 @@ def test_bst_exists(sample_bst): assert sample_bst -def test_bst_can_take_list(): - """Test that the BST can take a list as a param.""" +def test_bst_can_take_list_at_initialization(): + """Test that the BST can take a list.""" from bst import BST b = BST([1, 2, 3]) assert b.size() == 3 @@ -27,8 +27,8 @@ def test_bst_can_take_list(): assert b.right_depth == 2 -def test_bst_can_take_tuple(): - """Test that the BST can take a tuple as a param.""" +def test_bst_can_take_tuple_at_initialization(): + """Test that the BST can take a tuple.""" from bst import BST b = BST((1, 2, 3)) assert b.size() == 3 @@ -37,8 +37,8 @@ def test_bst_can_take_tuple(): assert b.right_depth == 2 -def test_bst_can_take_string(): - """Test that the BST can take a string as a param.""" +def test_bst_can_take_string_at_initialization(): + """Test that the BST can take a string.""" from bst import BST b = BST('abc') assert b.size() == 3 @@ -47,6 +47,15 @@ def test_bst_can_take_string(): assert b.right_depth == 2 +def test_insert_increases_depth(sample_bst): + """Test that the insert method increases the output of the depth method.""" + assert sample_bst.depth() == 0 + sample_bst.insert(1) + assert sample_bst.depth() == 0 + sample_bst.insert(2) + assert sample_bst.depth() == 1 + + def test_insert_increases_size(sample_bst): """Test that the insert method increases the output of the size method.""" assert sample_bst.size() == 0 @@ -65,15 +74,6 @@ def test_insert_increases_tree_size(sample_bst): assert sample_bst.tree_size == 2 -def test_insert_increases_depth(sample_bst): - """Test that the insert method increases the output of the depth method.""" - assert sample_bst.depth() == 0 - sample_bst.insert(1) - assert sample_bst.depth() == 0 - sample_bst.insert(2) - assert sample_bst.depth() == 1 - - def test_search_right(sample_bst): """Assert that the search method returns something and that it's a Node.""" from bst import Node From 7f33e610d2c73add3b75230d500296e7c988cb44 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 22:49:00 -0800 Subject: [PATCH 16/35] first part of README. --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 518a54e..b8c0438 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ -# data-structures -Data Structures in Python. Code Fellows 401. +# Data-Structures + +**Author**: Chelsea Dole + + +**Testing Tools**: pytest, pytest-cov + +## Data Structures: + +* **Binary Search Tree** — a BST is a "tree shaped" data structure containing nodes. Each node can have a maximum of two children or "leaves," and all node values are properly located based on its parents and siblings values. Nodes to the left of the "root"/head node have values smaller than the root. Those to the right have values larger than the root. There are no duplicate values. + +## Time Complexities: + + + From 05ea5d143c86da620120920c65d89e8a7947a468 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 23:12:56 -0800 Subject: [PATCH 17/35] completing README with runtime of all functions. --- README.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b8c0438..e9bc834 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,33 @@ **Author**: Chelsea Dole +**Resources/Shoutouts**: Nathan Moore (lab partner/amigo) **Testing Tools**: pytest, pytest-cov ## Data Structures: -* **Binary Search Tree** — a BST is a "tree shaped" data structure containing nodes. Each node can have a maximum of two children or "leaves," and all node values are properly located based on its parents and siblings values. Nodes to the left of the "root"/head node have values smaller than the root. Those to the right have values larger than the root. There are no duplicate values. +* **Binary Search Tree** — *a BST is a "tree shaped" data structure containing nodes. Each node can have a maximum of two children or "leaves," and all node values are properly located based on its parents and siblings values. Nodes to the left of the "root"/head node have values smaller than the root. Those to the right have values larger than the root. There are no duplicate values.* ## Time Complexities: +* balance() = *This BST function returns the balance (or size difference) between the left and right parts of the tree. Its runtime is O(1), because it always takes the same amount of time to run regardless of tree size, and only performs simple subtraction.* +* size() = *This BST function returns the number of nodes/leaves in a tree. Its runtime is O(1), because runtime never changes regardless of tree size. It only returns the value of tree_size, which is created at BST initialization, and changed during the insertion of nodes.* + +* insert() = *This BST function inserts a new node into a tree, and uses a helper function called find_home() to find its correctly sorted place in the tree. This function is, depending on the tree, anywhere between O(logn) and O(n), if it's a relatively balanced tree, every decision will reduce the number of nodes one has to traverse. But if it's a one-sided tree, one may look over every node -- making it O(n).* + +* search() = *This BST function is a reference to check_for_equivalence(), which is recursive, and has a runtime of O(n^2), because every time you're re-calling check_for_equivalence, it looks at every node's equivalence.* + +* contains() = *This BST function looks at search(), described above, and for the same reasons has runtime of O(n^2).* + +* depth() = *This BST function returns the number of "levels" that the tree has, by finding which of the two sides has the greatest depth, and returning that. It has a runtime of O(1), because no matter the size of the tree, it only performs a comparison operation.* + +* in_order() = *This BST traversal function traverses the tree and returns a generator that outputs the node values in numerical order. It has a runtime of O(n), not because you visit every node once (you visit them more than once here) but because the work you do/time you take is constant and grows constantly per node addition.* + +* pre_order() = *This BST traversal function returns a generator that outputs the node values in order of the furthest left parent, its left child, then its right child. This traveral then backs up to the parent, and repeats until the whole tree has been traversed. Like in_order, it has a runtime of O(n), not because you visit every node once (you visit them more than once here) but because the work you do/time you take is constant and grows constantly per node addition.* + +* post_order() = *This BST traversal function returns a generator that outputs the node values in order of the bottom-most left node, the bottom-most right node, and then those nodes' parent. Then it backs up, and repeats this action with the parent as a new child node, until the whole tree has been traversed. Like in_order and pre_order, it has a runtime of O(n), not because you visit every node once (you visit them more than once here) but because the work you do/time you take is constant and grows constantly per node addition.* + +* breadth_first() = *This BST traversal returns a generator that outputs the node values in order of their "levels". It produces first the root, then all nodes (left to right) in the first depth level, then all nodes (left to right) in the second depth level, et cetera. Like in_order, pre_order, and post_order, it has a runtime of O(n), not because you visit every node once (you visit them more than once here) but because the work you do/time you take is constant and grows constantly per node addition.* From 2a53b96985a5132e7b52b2974f32fbe36623a1e4 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 23:18:39 -0800 Subject: [PATCH 18/35] adding tox, tox.ini, and setup.py as required by tox. --- setup.py | 15 +++++++++++++++ bst.py => src/bst.py | 0 test_bst.py => src/test_bst.py | 0 tox.ini | 8 ++++++++ 4 files changed, 23 insertions(+) create mode 100644 setup.py rename bst.py => src/bst.py (100%) rename test_bst.py => src/test_bst.py (100%) create mode 100644 tox.ini diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..1284d5a --- /dev/null +++ b/setup.py @@ -0,0 +1,15 @@ +"""Setup module for Chelsea's data structures.""" + +from setuptools import setup + +setup( + name='Data structures', + description='Various data structures in python', + author='Chelsea Dole', + author_email='chelseadole@gmail', + package_dir={' ': 'src'}, + py_modules=['bst'], + install_requires=[], + extras_require={ + 'test': ['pytest', 'pytest-cov', 'pytest-watch', 'tox'], + 'development': ['ipython']}) diff --git a/bst.py b/src/bst.py similarity index 100% rename from bst.py rename to src/bst.py diff --git a/test_bst.py b/src/test_bst.py similarity index 100% rename from test_bst.py rename to src/test_bst.py diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..082b2bc --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py27, py36 + +[testenv] +commands = py.test --cov --cov-report term-missing +deps = + pytest + pytest-cov \ No newline at end of file From dec712d37e2165abc9b1f8878a0a043c5b15549e Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 23:28:05 -0800 Subject: [PATCH 19/35] adding if __name__ is main at bottom of BST. --- bst.py | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 bst.py diff --git a/bst.py b/bst.py new file mode 100644 index 0000000..4665490 --- /dev/null +++ b/bst.py @@ -0,0 +1,273 @@ +"""Implementation of a binary search tree data structure.""" +import timeit as time + + +class Node(object): + """Define the Node-class object.""" + + def __init__(self, value, left=None, right=None, parent=None): + """Constructor for the Node class.""" + self.val = value + self.left = left + self.right = right + self.parent = parent + self.depth = 0 + + +class BST(object): + """Define the BST-class object.""" + + def __init__(self, starting_values=None): + """Constructor for the BST class.""" + self.tree_size = 0 + self.left_depth = 0 + self.right_depth = 0 + self.visited = [] + + if starting_values is None: + self.root = None + + elif isinstance(starting_values, (list, str, tuple)): + self.root = Node(starting_values[0]) + self.tree_size += 1 + for i in range(len(starting_values) - 1): + self.insert(starting_values[i + 1]) + + else: + raise TypeError('Only iterables or None\ + are valid parameters!') + + def balance(self): + """Return the current balance of the BST.""" + return self.right_depth - self.left_depth + + def size(self): + """Return the current size of the BST.""" + return self.tree_size + + def insert(self, value): + """Insert a new node into the BST, and adjust the balance.""" + new_node = Node(value) + + if self.root: + if new_node.val > self.root.val: + if self.root.right: + self._find_home(new_node, self.root.right) + if new_node.depth > self.right_depth: + self.right_depth = new_node.depth + else: + new_node.parent = self.root + self.root.right = new_node + self.root.right.depth = 1 + if self.root.right.depth > self.right_depth: + self.right_depth = self.root.right.depth + self.tree_size += 1 + + elif new_node.val < self.root.val: + if self.root.left: + self._find_home(new_node, self.root.left) + if new_node.depth > self.left_depth: + self.left_depth = new_node.depth + else: + new_node.parent = self.root + self.root.left = new_node + self.root.left.depth = 1 + if self.root.left.depth > self.left_depth: + self.left_depth = self.root.left.depth + self.tree_size += 1 + else: + self.root = new_node + self.tree_size += 1 + + def _find_home(self, node_to_add, node_to_check): + """. + Check if the node_to_add belongs on the left or right + of the node_to_check, then place it there if that spot is empty, + otherwise recur. + """ + if node_to_add.val > node_to_check.val: + if node_to_check.right: + self._find_home(node_to_add, node_to_check.right) + else: + node_to_add.parent = node_to_check + node_to_check.right = node_to_add + node_to_check.right.depth = node_to_check.depth + 1 + self.tree_size += 1 + + elif node_to_add.val < node_to_check.val: + if node_to_check.left: + self._find_home(node_to_add, node_to_check.left) + else: + node_to_add.parent = node_to_check + node_to_check.left = node_to_add + node_to_check.left.depth = node_to_check.depth + 1 + self.tree_size += 1 + + def search(self, value): + """If a value is in the BST, return its node.""" + return self._check_for_equivalence(value, self.root) + + def contains(self, value): + """Return whether or not a value is in the BST.""" + return bool(self.search(value)) + + def _check_for_equivalence(self, value, node_to_check): + """. + Check if the value matches that of the node_to_check + if it does, return the node. If it doesn't, go left or right + as appropriate and recur. If you reach a dead end, return None. + """ + try: + if value == node_to_check.val: + return node_to_check + + except AttributeError: + return None + + if value > node_to_check.val and node_to_check.right: + return self._check_for_equivalence(value, node_to_check.right) + + elif value < node_to_check.val and node_to_check.left: + return self._check_for_equivalence(value, node_to_check.left) + + def depth(self): + """Return the depth of the BST.""" + if self.left_depth > self.right_depth: + return self.left_depth + return self.right_depth + + def in_order(self): + """Return a generator to perform an in-order traversal.""" + self.visited = [] + + if self.root is None: + raise IndexError("Tree is empty!") + + gen = self._in_order_gen() + return gen + + def _in_order_gen(self): + """Recursive helper method for in-order traversal.""" + current = self.root + + while len(self.visited) < self.tree_size: + if current.left: + if current.left.val not in self.visited: + current = current.left + continue + + if current.val not in self.visited: + self.visited.append(current.val) + yield current.val + + if current.right: + if current.right.val not in self.visited: + current = current.right + continue + + current = current.parent + + def pre_order(self): + """Return a generator to perform an pre-order traversal.""" + self.visited = [] + + if self.root is None: + raise IndexError("Tree is empty!") + + gen = self._pre_order_gen() + return gen + + def _pre_order_gen(self): + """Recursive helper method for pre-order traversal.""" + current = self.root + + while len(self.visited) < self.tree_size: + if current.val not in self.visited: + self.visited.append(current.val) + yield current.val + + if current.left: + if current.left.val not in self.visited: + current = current.left + continue + + if current.right: + if current.right.val not in self.visited: + current = current.right + continue + + current = current.parent + + def post_order(self): + """Return a generator to perform an post-order traversal.""" + self.visited = [] + + if self.root is None: + raise IndexError("Tree is empty!") + + gen = self._post_order_gen() + return gen + + def _post_order_gen(self): + """Recursive helper method for post-order traversal.""" + current = self.root + + while len(self.visited) < self.tree_size: + if current.left: + if current.left.val not in self.visited: + current = current.left + continue + + if current.right: + if current.right.val not in self.visited: + current = current.right + continue + + if current.val not in self.visited: + self.visited.append(current.val) + yield current.val + + current = current.parent + + def breadth_first(self): + """Return a generator to perform a breadth-first traversal.""" + self.visited = [] + + if self.root is None: + raise IndexError("Tree is empty!") + + gen = self._breadth_first_gen(self.root) + return gen + + def _breadth_first_gen(self, root_node): + """Helper generator for breadth-first traversal.""" + queue = [self.root] + while queue: + current = queue[0] + yield current.val + queue = queue[1:] + + if current not in self.visited: + self.visited.append(current) + + if current.left: + if current.left not in self.visited: + queue.append(current.left) + + if current.right: + if current.right not in self.visited: + queue.append(current.right) + + +if __name__ == '__main__': # pragma: no cover + left_bigger = BST([6, 5, 4, 3, 2, 1]) + right_bigger = BST([1, 2, 3, 4, 5, 6]) + bal_tree = BST([20, 12, 10, 1, 11, 16, 30, 42, 28, 27]) + + left_bigger = time.timeit("left_bigger.search(5)", setup="from __main__ import left_bigger") + right_bigger = time.timeit("right_bigger.search(5)", setup="from __main__ import right_bigger") + bal_tree = time.timeit("bal_tree.search(8)", setup="from __main__ import bal_tree") + + print('Left-Skewed Search Time: ', left_bigger) + print('Right-Skewed Search Time: ', right_bigger) + print('Balanced Search Time: ', bal_tree) From a054665dede05a452d27e28642d7d394761368a6 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sat, 25 Nov 2017 23:32:49 -0800 Subject: [PATCH 20/35] getting rid of duplicate test_bst file outside of src/ directory. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1284d5a..6751995 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ author_email='chelseadole@gmail', package_dir={' ': 'src'}, py_modules=['bst'], - install_requires=[], + install_requires=['timeit'], extras_require={ 'test': ['pytest', 'pytest-cov', 'pytest-watch', 'tox'], 'development': ['ipython']}) From 2271aa9730d0d5a8340f004f33412c086699135a Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 14:06:32 -0800 Subject: [PATCH 21/35] last delete changes. --- bst.py | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/bst.py b/bst.py index 4665490..fc86de8 100644 --- a/bst.py +++ b/bst.py @@ -258,6 +258,126 @@ def _breadth_first_gen(self, root_node): if current.right not in self.visited: queue.append(current.right) + def delete(self, val): + """Delete the node with the given value from the tree.""" + node = self.search(val) + if node is None: + return + + self.tree_size -= 1 + is_root = node == self.root + first_move_right = val > self.root.val + last_move_right = node == node.parent.right + + if node.left is None and node.right is None: + if is_root: + self.root = None + return + if last_move_right: + node.parent.right = None + # self._rebalance(node.parent) + return + node.parent.left = None + # self._rebalance(node.parent) + return + + if node.left is None: + if is_root: + node.right.parent = None + self.root = node.right + self.root.depth = 0 + self.right_depth = self._reassess_depths(self.root.right) + return + if last_move_right: + node.parent.right = node.right + node.right.parent = node.parent + + else: + node.parent.left = node.right + node.right.parent = node.parent + + if first_move_right: + self.right_depth = self._reassess_depths(node.right) + # self._rebalance(node.parent) + return + self.left_depth = self._reassess_depths(node.right) + # self._rebalance(node.parent) + return + + if node.right is None: + if is_root: + node.left.parent = None + self.root = node.left + self.root.depth = 0 + self.left_depth = self._reassess_depths(self.root.left) + return + if last_move_right: + node.parent.right = node.left + node.left.parent = node.parent + + node.parent.left = node.left + node.left.parent = node.parent + if first_move_right: + self.right_depth = self._reassess_depths(node.left) + # self._rebalance(node.parent) + return + self.left_depth = self._reassess_depths(node.left) + # self._rebalance(node.parent) + return + else: + replacement_node = self._locate_replacement_node(node) + self.delete(replacement_node.val) + + if is_root: + self.root = replacement_node + replacement_node.parent = None + replacement_node.depth = 0 + + else: + replacement_node.parent = node.parent + replacement_node.depth = node.depth + + replacement_node.left = node.left + replacement_node.right = node.right + node.left.parent = replacement_node + node.right.parent = replacement_node + if first_move_right or is_root: + self.right_depth = self._reassess_depths(self.root.right) + + else: + self.left_depth = self._reassess_depths(self.root.left) + + # if node.parent: + # self._rebalance(node.parent) + return + + def _reassess_depths(self, starting_node): + """Fix the depth of nodes below the starting_node an return the max_depth.""" + self.max_depth = 0 + + if starting_node: + self.visited = [] + queue = [starting_node] + while queue: + current = queue[0] + current.depth = current.parent.depth + 1 + if current.depth > self.max_depth: + self.max_depth = current.depth + queue = queue[1:] + + if current not in self.visited: + self.visited.append(current) + + if current.left: + if current.left not in self.visited: + queue.append(current.left) + + if current.right: + if current.right not in self.visited: + queue.append(current.right) + + return self.max_depth - starting_node.parent.depth + if __name__ == '__main__': # pragma: no cover left_bigger = BST([6, 5, 4, 3, 2, 1]) From c8829b4edbe506789bc7252b9d0d48b01bf7e079 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:17:07 -0800 Subject: [PATCH 22/35] adding balance(). updated version, using _reassess depths. --- bst.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/bst.py b/bst.py index fc86de8..809428a 100644 --- a/bst.py +++ b/bst.py @@ -37,9 +37,21 @@ def __init__(self, starting_values=None): raise TypeError('Only iterables or None\ are valid parameters!') - def balance(self): - """Return the current balance of the BST.""" - return self.right_depth - self.left_depth + def balance(self, starting_node=None): + """Return the current balance of a tree or subtree.""" + if not starting_node: + return self.right_depth - self.left_depth + + r_depth = 0 + l_depth = 0 + + if starting_node.right: + r_depth += self._reassess_depths(starting_node.right) + + if starting_node.left: + l_depth += self._reassess_depths(starting_node.left) + + return r_depth - l_depth def size(self): """Return the current size of the BST.""" From 634f1e23e4bd1dec86602ed6d891dea9f2391cb6 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:18:43 -0800 Subject: [PATCH 23/35] updating depth() fn. --- bst.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bst.py b/bst.py index 809428a..6d49a39 100644 --- a/bst.py +++ b/bst.py @@ -104,7 +104,7 @@ def _find_home(self, node_to_add, node_to_check): node_to_add.parent = node_to_check node_to_check.right = node_to_add node_to_check.right.depth = node_to_check.depth + 1 - self.tree_size += 1 + # self._rebalance(node_to_add.parent) elif node_to_add.val < node_to_check.val: if node_to_check.left: @@ -113,7 +113,7 @@ def _find_home(self, node_to_add, node_to_check): node_to_add.parent = node_to_check node_to_check.left = node_to_add node_to_check.left.depth = node_to_check.depth + 1 - self.tree_size += 1 + # self._rebalance(node_to_add.parent) def search(self, value): """If a value is in the BST, return its node.""" @@ -142,11 +142,12 @@ def _check_for_equivalence(self, value, node_to_check): elif value < node_to_check.val and node_to_check.left: return self._check_for_equivalence(value, node_to_check.left) - def depth(self): - """Return the depth of the BST.""" - if self.left_depth > self.right_depth: - return self.left_depth - return self.right_depth + def depth(self, starting_node=None): + """Return the depth of the tree or subtree.""" + if not starting_node: + if self.left_depth > self.right_depth: + return self.left_depth + return self.right_depth def in_order(self): """Return a generator to perform an in-order traversal.""" @@ -156,7 +157,7 @@ def in_order(self): raise IndexError("Tree is empty!") gen = self._in_order_gen() - return gen + return geng def _in_order_gen(self): """Recursive helper method for in-order traversal.""" From bb390644edecdaaa87f1c940797a71378f83a122 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:20:16 -0800 Subject: [PATCH 24/35] adding new version of _locate_replacement node --- bst.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bst.py b/bst.py index 6d49a39..90e73e4 100644 --- a/bst.py +++ b/bst.py @@ -391,6 +391,13 @@ def _reassess_depths(self, starting_node): return self.max_depth - starting_node.parent.depth + def _locate_replacement_node(self, starting_node): + """Return the lowest-valued node on the right side of the sub-tree.""" + current = starting_node.right + while current.left: + current = current.left + return current + if __name__ == '__main__': # pragma: no cover left_bigger = BST([6, 5, 4, 3, 2, 1]) From 51d179a9c09d0614f4dedf5ef24ef334276efafd Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:20:46 -0800 Subject: [PATCH 25/35] adding OG rebalance, none of that re-re shit. --- bst.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/bst.py b/bst.py index 90e73e4..157dd18 100644 --- a/bst.py +++ b/bst.py @@ -398,6 +398,34 @@ def _locate_replacement_node(self, starting_node): current = current.left return current + def _rebalance(self, node): + """Rebalance a subtree, and if its root has a parent, recur on it.""" + node_balance = self.balance(node) + + if node_balance == 2: + child_balance = self.balance(node.right) + + if child_balance == 1: + self._rotate_left(node) + + if child_balance == -1: + self._rotate_right(node.right) + self._rotate_left(node) + + if node_balance == -2: + child_balance = self.balance(node.left) + + if child_balance == 1: + self._rotate_left(node.left) + self._rotate_right(node) + + if child_balance == -1: + self._rotate_right(node) + + # if node.parent: + # self._rebalance(node.parent) + + if __name__ == '__main__': # pragma: no cover left_bigger = BST([6, 5, 4, 3, 2, 1]) From 10c4d38033215a3ed4f6c3a87b98713cdb085a1f Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:21:02 -0800 Subject: [PATCH 26/35] actually reworking rebalance, i think i messed it up. --- bst.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/bst.py b/bst.py index 157dd18..6a19c92 100644 --- a/bst.py +++ b/bst.py @@ -412,19 +412,6 @@ def _rebalance(self, node): self._rotate_right(node.right) self._rotate_left(node) - if node_balance == -2: - child_balance = self.balance(node.left) - - if child_balance == 1: - self._rotate_left(node.left) - self._rotate_right(node) - - if child_balance == -1: - self._rotate_right(node) - - # if node.parent: - # self._rebalance(node.parent) - if __name__ == '__main__': # pragma: no cover From c41458f33340771659a0bec19d5efb77ba7f2a19 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:21:27 -0800 Subject: [PATCH 27/35] JK. Adding changes we made during code review on thurs, and putting last commit material back in. --- bst.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bst.py b/bst.py index 6a19c92..157dd18 100644 --- a/bst.py +++ b/bst.py @@ -412,6 +412,19 @@ def _rebalance(self, node): self._rotate_right(node.right) self._rotate_left(node) + if node_balance == -2: + child_balance = self.balance(node.left) + + if child_balance == 1: + self._rotate_left(node.left) + self._rotate_right(node) + + if child_balance == -1: + self._rotate_right(node) + + # if node.parent: + # self._rebalance(node.parent) + if __name__ == '__main__': # pragma: no cover From b42566dc140f107fffb83445c6af97f80b7fe13c Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:21:48 -0800 Subject: [PATCH 28/35] adding rotate left --- bst.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bst.py b/bst.py index 157dd18..2ee71ea 100644 --- a/bst.py +++ b/bst.py @@ -425,6 +425,25 @@ def _rebalance(self, node): # if node.parent: # self._rebalance(node.parent) + def _rotate_left(self, node): + """Rotate a node leftwards around its right child.""" + pivot_node = node.right + + if node.parent: + node.parent.left = pivot_node + + else: + self.root = pivot_node + + pivot_node.parent = node.parent + node.parent = pivot_node + + if pivot_node.left: + pivot_node.left.parent = node + + node.right = pivot_node.left + pivot_node.left = node + if __name__ == '__main__': # pragma: no cover From 5993eb2366c294f926cd65cab00eae475976a3c5 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:22:19 -0800 Subject: [PATCH 29/35] adding rotate left, basically a reflection of rotate right --- bst.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bst.py b/bst.py index 2ee71ea..f8872b7 100644 --- a/bst.py +++ b/bst.py @@ -444,6 +444,25 @@ def _rotate_left(self, node): node.right = pivot_node.left pivot_node.left = node + def _rotate_right(self, node): + """Rotate a node rightwards around its left child.""" + pivot_node = node.left + + if node.parent: + node.parent.right = pivot_node + + else: + self.root = pivot_node + + pivot_node.parent = node.parent + node.parent = pivot_node + + if pivot_node.right: + pivot_node.right.parent = node + + node.left = pivot_node.right + pivot_node.right = node + if __name__ == '__main__': # pragma: no cover From ec64c36a7e96788188550617d1333698bf7c09af Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:28:18 -0800 Subject: [PATCH 30/35] just discovered that i was adding changes to the wrong bst file, so i changed that back to the src/bst.py file. --- bst.py | 1 - src/bst.py | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 231 insertions(+), 12 deletions(-) diff --git a/bst.py b/bst.py index f8872b7..6ad18df 100644 --- a/bst.py +++ b/bst.py @@ -464,7 +464,6 @@ def _rotate_right(self, node): pivot_node.right = node - if __name__ == '__main__': # pragma: no cover left_bigger = BST([6, 5, 4, 3, 2, 1]) right_bigger = BST([1, 2, 3, 4, 5, 6]) diff --git a/src/bst.py b/src/bst.py index 96faef5..6ad18df 100644 --- a/src/bst.py +++ b/src/bst.py @@ -1,4 +1,5 @@ """Implementation of a binary search tree data structure.""" +import timeit as time class Node(object): @@ -36,9 +37,21 @@ def __init__(self, starting_values=None): raise TypeError('Only iterables or None\ are valid parameters!') - def balance(self): - """Return the current balance of the BST.""" - return self.right_depth - self.left_depth + def balance(self, starting_node=None): + """Return the current balance of a tree or subtree.""" + if not starting_node: + return self.right_depth - self.left_depth + + r_depth = 0 + l_depth = 0 + + if starting_node.right: + r_depth += self._reassess_depths(starting_node.right) + + if starting_node.left: + l_depth += self._reassess_depths(starting_node.left) + + return r_depth - l_depth def size(self): """Return the current size of the BST.""" @@ -91,7 +104,7 @@ def _find_home(self, node_to_add, node_to_check): node_to_add.parent = node_to_check node_to_check.right = node_to_add node_to_check.right.depth = node_to_check.depth + 1 - self.tree_size += 1 + # self._rebalance(node_to_add.parent) elif node_to_add.val < node_to_check.val: if node_to_check.left: @@ -100,7 +113,7 @@ def _find_home(self, node_to_add, node_to_check): node_to_add.parent = node_to_check node_to_check.left = node_to_add node_to_check.left.depth = node_to_check.depth + 1 - self.tree_size += 1 + # self._rebalance(node_to_add.parent) def search(self, value): """If a value is in the BST, return its node.""" @@ -129,11 +142,12 @@ def _check_for_equivalence(self, value, node_to_check): elif value < node_to_check.val and node_to_check.left: return self._check_for_equivalence(value, node_to_check.left) - def depth(self): - """Return the depth of the BST.""" - if self.left_depth > self.right_depth: - return self.left_depth - return self.right_depth + def depth(self, starting_node=None): + """Return the depth of the tree or subtree.""" + if not starting_node: + if self.left_depth > self.right_depth: + return self.left_depth + return self.right_depth def in_order(self): """Return a generator to perform an in-order traversal.""" @@ -143,7 +157,7 @@ def in_order(self): raise IndexError("Tree is empty!") gen = self._in_order_gen() - return gen + return geng def _in_order_gen(self): """Recursive helper method for in-order traversal.""" @@ -256,3 +270,209 @@ def _breadth_first_gen(self, root_node): if current.right: if current.right not in self.visited: queue.append(current.right) + + def delete(self, val): + """Delete the node with the given value from the tree.""" + node = self.search(val) + if node is None: + return + + self.tree_size -= 1 + is_root = node == self.root + first_move_right = val > self.root.val + last_move_right = node == node.parent.right + + if node.left is None and node.right is None: + if is_root: + self.root = None + return + if last_move_right: + node.parent.right = None + # self._rebalance(node.parent) + return + node.parent.left = None + # self._rebalance(node.parent) + return + + if node.left is None: + if is_root: + node.right.parent = None + self.root = node.right + self.root.depth = 0 + self.right_depth = self._reassess_depths(self.root.right) + return + if last_move_right: + node.parent.right = node.right + node.right.parent = node.parent + + else: + node.parent.left = node.right + node.right.parent = node.parent + + if first_move_right: + self.right_depth = self._reassess_depths(node.right) + # self._rebalance(node.parent) + return + self.left_depth = self._reassess_depths(node.right) + # self._rebalance(node.parent) + return + + if node.right is None: + if is_root: + node.left.parent = None + self.root = node.left + self.root.depth = 0 + self.left_depth = self._reassess_depths(self.root.left) + return + if last_move_right: + node.parent.right = node.left + node.left.parent = node.parent + + node.parent.left = node.left + node.left.parent = node.parent + if first_move_right: + self.right_depth = self._reassess_depths(node.left) + # self._rebalance(node.parent) + return + self.left_depth = self._reassess_depths(node.left) + # self._rebalance(node.parent) + return + else: + replacement_node = self._locate_replacement_node(node) + self.delete(replacement_node.val) + + if is_root: + self.root = replacement_node + replacement_node.parent = None + replacement_node.depth = 0 + + else: + replacement_node.parent = node.parent + replacement_node.depth = node.depth + + replacement_node.left = node.left + replacement_node.right = node.right + node.left.parent = replacement_node + node.right.parent = replacement_node + if first_move_right or is_root: + self.right_depth = self._reassess_depths(self.root.right) + + else: + self.left_depth = self._reassess_depths(self.root.left) + + # if node.parent: + # self._rebalance(node.parent) + return + + def _reassess_depths(self, starting_node): + """Fix the depth of nodes below the starting_node an return the max_depth.""" + self.max_depth = 0 + + if starting_node: + self.visited = [] + queue = [starting_node] + while queue: + current = queue[0] + current.depth = current.parent.depth + 1 + if current.depth > self.max_depth: + self.max_depth = current.depth + queue = queue[1:] + + if current not in self.visited: + self.visited.append(current) + + if current.left: + if current.left not in self.visited: + queue.append(current.left) + + if current.right: + if current.right not in self.visited: + queue.append(current.right) + + return self.max_depth - starting_node.parent.depth + + def _locate_replacement_node(self, starting_node): + """Return the lowest-valued node on the right side of the sub-tree.""" + current = starting_node.right + while current.left: + current = current.left + return current + + def _rebalance(self, node): + """Rebalance a subtree, and if its root has a parent, recur on it.""" + node_balance = self.balance(node) + + if node_balance == 2: + child_balance = self.balance(node.right) + + if child_balance == 1: + self._rotate_left(node) + + if child_balance == -1: + self._rotate_right(node.right) + self._rotate_left(node) + + if node_balance == -2: + child_balance = self.balance(node.left) + + if child_balance == 1: + self._rotate_left(node.left) + self._rotate_right(node) + + if child_balance == -1: + self._rotate_right(node) + + # if node.parent: + # self._rebalance(node.parent) + + def _rotate_left(self, node): + """Rotate a node leftwards around its right child.""" + pivot_node = node.right + + if node.parent: + node.parent.left = pivot_node + + else: + self.root = pivot_node + + pivot_node.parent = node.parent + node.parent = pivot_node + + if pivot_node.left: + pivot_node.left.parent = node + + node.right = pivot_node.left + pivot_node.left = node + + def _rotate_right(self, node): + """Rotate a node rightwards around its left child.""" + pivot_node = node.left + + if node.parent: + node.parent.right = pivot_node + + else: + self.root = pivot_node + + pivot_node.parent = node.parent + node.parent = pivot_node + + if pivot_node.right: + pivot_node.right.parent = node + + node.left = pivot_node.right + pivot_node.right = node + + +if __name__ == '__main__': # pragma: no cover + left_bigger = BST([6, 5, 4, 3, 2, 1]) + right_bigger = BST([1, 2, 3, 4, 5, 6]) + bal_tree = BST([20, 12, 10, 1, 11, 16, 30, 42, 28, 27]) + + left_bigger = time.timeit("left_bigger.search(5)", setup="from __main__ import left_bigger") + right_bigger = time.timeit("right_bigger.search(5)", setup="from __main__ import right_bigger") + bal_tree = time.timeit("bal_tree.search(8)", setup="from __main__ import bal_tree") + + print('Left-Skewed Search Time: ', left_bigger) + print('Right-Skewed Search Time: ', right_bigger) + print('Balanced Search Time: ', bal_tree) From 2fb2675897af9ef4b041a5d6136ecd50bb08eafe Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:28:40 -0800 Subject: [PATCH 31/35] deleting old BST version. --- bst.py | 478 --------------------------------------------------------- 1 file changed, 478 deletions(-) delete mode 100644 bst.py diff --git a/bst.py b/bst.py deleted file mode 100644 index 6ad18df..0000000 --- a/bst.py +++ /dev/null @@ -1,478 +0,0 @@ -"""Implementation of a binary search tree data structure.""" -import timeit as time - - -class Node(object): - """Define the Node-class object.""" - - def __init__(self, value, left=None, right=None, parent=None): - """Constructor for the Node class.""" - self.val = value - self.left = left - self.right = right - self.parent = parent - self.depth = 0 - - -class BST(object): - """Define the BST-class object.""" - - def __init__(self, starting_values=None): - """Constructor for the BST class.""" - self.tree_size = 0 - self.left_depth = 0 - self.right_depth = 0 - self.visited = [] - - if starting_values is None: - self.root = None - - elif isinstance(starting_values, (list, str, tuple)): - self.root = Node(starting_values[0]) - self.tree_size += 1 - for i in range(len(starting_values) - 1): - self.insert(starting_values[i + 1]) - - else: - raise TypeError('Only iterables or None\ - are valid parameters!') - - def balance(self, starting_node=None): - """Return the current balance of a tree or subtree.""" - if not starting_node: - return self.right_depth - self.left_depth - - r_depth = 0 - l_depth = 0 - - if starting_node.right: - r_depth += self._reassess_depths(starting_node.right) - - if starting_node.left: - l_depth += self._reassess_depths(starting_node.left) - - return r_depth - l_depth - - def size(self): - """Return the current size of the BST.""" - return self.tree_size - - def insert(self, value): - """Insert a new node into the BST, and adjust the balance.""" - new_node = Node(value) - - if self.root: - if new_node.val > self.root.val: - if self.root.right: - self._find_home(new_node, self.root.right) - if new_node.depth > self.right_depth: - self.right_depth = new_node.depth - else: - new_node.parent = self.root - self.root.right = new_node - self.root.right.depth = 1 - if self.root.right.depth > self.right_depth: - self.right_depth = self.root.right.depth - self.tree_size += 1 - - elif new_node.val < self.root.val: - if self.root.left: - self._find_home(new_node, self.root.left) - if new_node.depth > self.left_depth: - self.left_depth = new_node.depth - else: - new_node.parent = self.root - self.root.left = new_node - self.root.left.depth = 1 - if self.root.left.depth > self.left_depth: - self.left_depth = self.root.left.depth - self.tree_size += 1 - else: - self.root = new_node - self.tree_size += 1 - - def _find_home(self, node_to_add, node_to_check): - """. - Check if the node_to_add belongs on the left or right - of the node_to_check, then place it there if that spot is empty, - otherwise recur. - """ - if node_to_add.val > node_to_check.val: - if node_to_check.right: - self._find_home(node_to_add, node_to_check.right) - else: - node_to_add.parent = node_to_check - node_to_check.right = node_to_add - node_to_check.right.depth = node_to_check.depth + 1 - # self._rebalance(node_to_add.parent) - - elif node_to_add.val < node_to_check.val: - if node_to_check.left: - self._find_home(node_to_add, node_to_check.left) - else: - node_to_add.parent = node_to_check - node_to_check.left = node_to_add - node_to_check.left.depth = node_to_check.depth + 1 - # self._rebalance(node_to_add.parent) - - def search(self, value): - """If a value is in the BST, return its node.""" - return self._check_for_equivalence(value, self.root) - - def contains(self, value): - """Return whether or not a value is in the BST.""" - return bool(self.search(value)) - - def _check_for_equivalence(self, value, node_to_check): - """. - Check if the value matches that of the node_to_check - if it does, return the node. If it doesn't, go left or right - as appropriate and recur. If you reach a dead end, return None. - """ - try: - if value == node_to_check.val: - return node_to_check - - except AttributeError: - return None - - if value > node_to_check.val and node_to_check.right: - return self._check_for_equivalence(value, node_to_check.right) - - elif value < node_to_check.val and node_to_check.left: - return self._check_for_equivalence(value, node_to_check.left) - - def depth(self, starting_node=None): - """Return the depth of the tree or subtree.""" - if not starting_node: - if self.left_depth > self.right_depth: - return self.left_depth - return self.right_depth - - def in_order(self): - """Return a generator to perform an in-order traversal.""" - self.visited = [] - - if self.root is None: - raise IndexError("Tree is empty!") - - gen = self._in_order_gen() - return geng - - def _in_order_gen(self): - """Recursive helper method for in-order traversal.""" - current = self.root - - while len(self.visited) < self.tree_size: - if current.left: - if current.left.val not in self.visited: - current = current.left - continue - - if current.val not in self.visited: - self.visited.append(current.val) - yield current.val - - if current.right: - if current.right.val not in self.visited: - current = current.right - continue - - current = current.parent - - def pre_order(self): - """Return a generator to perform an pre-order traversal.""" - self.visited = [] - - if self.root is None: - raise IndexError("Tree is empty!") - - gen = self._pre_order_gen() - return gen - - def _pre_order_gen(self): - """Recursive helper method for pre-order traversal.""" - current = self.root - - while len(self.visited) < self.tree_size: - if current.val not in self.visited: - self.visited.append(current.val) - yield current.val - - if current.left: - if current.left.val not in self.visited: - current = current.left - continue - - if current.right: - if current.right.val not in self.visited: - current = current.right - continue - - current = current.parent - - def post_order(self): - """Return a generator to perform an post-order traversal.""" - self.visited = [] - - if self.root is None: - raise IndexError("Tree is empty!") - - gen = self._post_order_gen() - return gen - - def _post_order_gen(self): - """Recursive helper method for post-order traversal.""" - current = self.root - - while len(self.visited) < self.tree_size: - if current.left: - if current.left.val not in self.visited: - current = current.left - continue - - if current.right: - if current.right.val not in self.visited: - current = current.right - continue - - if current.val not in self.visited: - self.visited.append(current.val) - yield current.val - - current = current.parent - - def breadth_first(self): - """Return a generator to perform a breadth-first traversal.""" - self.visited = [] - - if self.root is None: - raise IndexError("Tree is empty!") - - gen = self._breadth_first_gen(self.root) - return gen - - def _breadth_first_gen(self, root_node): - """Helper generator for breadth-first traversal.""" - queue = [self.root] - while queue: - current = queue[0] - yield current.val - queue = queue[1:] - - if current not in self.visited: - self.visited.append(current) - - if current.left: - if current.left not in self.visited: - queue.append(current.left) - - if current.right: - if current.right not in self.visited: - queue.append(current.right) - - def delete(self, val): - """Delete the node with the given value from the tree.""" - node = self.search(val) - if node is None: - return - - self.tree_size -= 1 - is_root = node == self.root - first_move_right = val > self.root.val - last_move_right = node == node.parent.right - - if node.left is None and node.right is None: - if is_root: - self.root = None - return - if last_move_right: - node.parent.right = None - # self._rebalance(node.parent) - return - node.parent.left = None - # self._rebalance(node.parent) - return - - if node.left is None: - if is_root: - node.right.parent = None - self.root = node.right - self.root.depth = 0 - self.right_depth = self._reassess_depths(self.root.right) - return - if last_move_right: - node.parent.right = node.right - node.right.parent = node.parent - - else: - node.parent.left = node.right - node.right.parent = node.parent - - if first_move_right: - self.right_depth = self._reassess_depths(node.right) - # self._rebalance(node.parent) - return - self.left_depth = self._reassess_depths(node.right) - # self._rebalance(node.parent) - return - - if node.right is None: - if is_root: - node.left.parent = None - self.root = node.left - self.root.depth = 0 - self.left_depth = self._reassess_depths(self.root.left) - return - if last_move_right: - node.parent.right = node.left - node.left.parent = node.parent - - node.parent.left = node.left - node.left.parent = node.parent - if first_move_right: - self.right_depth = self._reassess_depths(node.left) - # self._rebalance(node.parent) - return - self.left_depth = self._reassess_depths(node.left) - # self._rebalance(node.parent) - return - else: - replacement_node = self._locate_replacement_node(node) - self.delete(replacement_node.val) - - if is_root: - self.root = replacement_node - replacement_node.parent = None - replacement_node.depth = 0 - - else: - replacement_node.parent = node.parent - replacement_node.depth = node.depth - - replacement_node.left = node.left - replacement_node.right = node.right - node.left.parent = replacement_node - node.right.parent = replacement_node - if first_move_right or is_root: - self.right_depth = self._reassess_depths(self.root.right) - - else: - self.left_depth = self._reassess_depths(self.root.left) - - # if node.parent: - # self._rebalance(node.parent) - return - - def _reassess_depths(self, starting_node): - """Fix the depth of nodes below the starting_node an return the max_depth.""" - self.max_depth = 0 - - if starting_node: - self.visited = [] - queue = [starting_node] - while queue: - current = queue[0] - current.depth = current.parent.depth + 1 - if current.depth > self.max_depth: - self.max_depth = current.depth - queue = queue[1:] - - if current not in self.visited: - self.visited.append(current) - - if current.left: - if current.left not in self.visited: - queue.append(current.left) - - if current.right: - if current.right not in self.visited: - queue.append(current.right) - - return self.max_depth - starting_node.parent.depth - - def _locate_replacement_node(self, starting_node): - """Return the lowest-valued node on the right side of the sub-tree.""" - current = starting_node.right - while current.left: - current = current.left - return current - - def _rebalance(self, node): - """Rebalance a subtree, and if its root has a parent, recur on it.""" - node_balance = self.balance(node) - - if node_balance == 2: - child_balance = self.balance(node.right) - - if child_balance == 1: - self._rotate_left(node) - - if child_balance == -1: - self._rotate_right(node.right) - self._rotate_left(node) - - if node_balance == -2: - child_balance = self.balance(node.left) - - if child_balance == 1: - self._rotate_left(node.left) - self._rotate_right(node) - - if child_balance == -1: - self._rotate_right(node) - - # if node.parent: - # self._rebalance(node.parent) - - def _rotate_left(self, node): - """Rotate a node leftwards around its right child.""" - pivot_node = node.right - - if node.parent: - node.parent.left = pivot_node - - else: - self.root = pivot_node - - pivot_node.parent = node.parent - node.parent = pivot_node - - if pivot_node.left: - pivot_node.left.parent = node - - node.right = pivot_node.left - pivot_node.left = node - - def _rotate_right(self, node): - """Rotate a node rightwards around its left child.""" - pivot_node = node.left - - if node.parent: - node.parent.right = pivot_node - - else: - self.root = pivot_node - - pivot_node.parent = node.parent - node.parent = pivot_node - - if pivot_node.right: - pivot_node.right.parent = node - - node.left = pivot_node.right - pivot_node.right = node - - -if __name__ == '__main__': # pragma: no cover - left_bigger = BST([6, 5, 4, 3, 2, 1]) - right_bigger = BST([1, 2, 3, 4, 5, 6]) - bal_tree = BST([20, 12, 10, 1, 11, 16, 30, 42, 28, 27]) - - left_bigger = time.timeit("left_bigger.search(5)", setup="from __main__ import left_bigger") - right_bigger = time.timeit("right_bigger.search(5)", setup="from __main__ import right_bigger") - bal_tree = time.timeit("bal_tree.search(8)", setup="from __main__ import bal_tree") - - print('Left-Skewed Search Time: ', left_bigger) - print('Right-Skewed Search Time: ', right_bigger) - print('Balanced Search Time: ', bal_tree) From 026f4c4597fc1bbadf023528caf5f3908babaf3d Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 16:42:14 -0800 Subject: [PATCH 32/35] fixed tree_size, replacing two lines. --- src/bst.py | 5 ++++- src/test_bst.py | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index 6ad18df..c21a502 100644 --- a/src/bst.py +++ b/src/bst.py @@ -67,6 +67,7 @@ def insert(self, value): self._find_home(new_node, self.root.right) if new_node.depth > self.right_depth: self.right_depth = new_node.depth + self.tree_size += 1 else: new_node.parent = self.root self.root.right = new_node @@ -80,6 +81,8 @@ def insert(self, value): self._find_home(new_node, self.root.left) if new_node.depth > self.left_depth: self.left_depth = new_node.depth + self.tree_size += 1 + else: new_node.parent = self.root self.root.left = new_node @@ -157,7 +160,7 @@ def in_order(self): raise IndexError("Tree is empty!") gen = self._in_order_gen() - return geng + return gen def _in_order_gen(self): """Recursive helper method for in-order traversal.""" diff --git a/src/test_bst.py b/src/test_bst.py index 77b764f..475e845 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -361,3 +361,10 @@ def test_breadth_first_sample_tree(): for i in range(10): output.append(next(gen)) assert output == [20, 12, 30, 10, 16, 28, 42, 1, 11, 27] + + +def test_delete_node_that_doesnt_exist(): + """Test deleting a nonexistant node from BST.""" + tree = BST([5, 3, 2, 15, 44, 100]) + assert tree.delete(20) is None + From a1b0e7b8278f90e598e16aa6ab953211fcb92ff4 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 17:26:42 -0800 Subject: [PATCH 33/35] adding last few tests for delete() BST method. --- src/bst.py | 537 +++++++++++++++++++++++++++++++++++++++++++++--- src/test_bst.py | 54 ++++- 2 files changed, 554 insertions(+), 37 deletions(-) diff --git a/src/bst.py b/src/bst.py index c21a502..8e24e3e 100644 --- a/src/bst.py +++ b/src/bst.py @@ -1,5 +1,484 @@ -"""Implementation of a binary search tree data structure.""" -import timeit as time +# """Implementation of a binary search tree data structure.""" +# import timeit as time + + +# class Node(object): +# """Define the Node-class object.""" + +# def __init__(self, value, left=None, right=None, parent=None): +# """Constructor for the Node class.""" +# self.val = value +# self.left = left +# self.right = right +# self.parent = parent +# self.depth = 0 + + +# class BST(object): +# """Define the BST-class object.""" + +# def __init__(self, starting_values=None): +# """Constructor for the BST class.""" +# self.tree_size = 0 +# self.left_depth = 0 +# self.right_depth = 0 +# self.visited = [] + +# if starting_values is None: +# self.root = None + +# elif isinstance(starting_values, (list, str, tuple)): +# self.root = Node(starting_values[0]) +# self.tree_size += 1 +# for i in range(len(starting_values) - 1): +# self.insert(starting_values[i + 1]) + +# else: +# raise TypeError('Only iterables or None\ +# are valid parameters!') + +# def balance(self, starting_node=None): +# """Return the current balance of a tree or subtree.""" +# if not starting_node: +# return self.right_depth - self.left_depth + +# r_depth = 0 +# l_depth = 0 + +# if starting_node.right: +# r_depth += self._reassess_depths(starting_node.right) + +# if starting_node.left: +# l_depth += self._reassess_depths(starting_node.left) + +# return r_depth - l_depth + +# def size(self): +# """Return the current size of the BST.""" +# return self.tree_size + +# def insert(self, value): +# """Insert a new node into the BST, and adjust the balance.""" +# new_node = Node(value) + +# if self.root: +# if new_node.val > self.root.val: +# if self.root.right: +# self._find_home(new_node, self.root.right) +# if new_node.depth > self.right_depth: +# self.right_depth = new_node.depth +# self.tree_size += 1 +# else: +# new_node.parent = self.root +# self.root.right = new_node +# self.root.right.depth = 1 +# if self.root.right.depth > self.right_depth: +# self.right_depth = self.root.right.depth +# self.tree_size += 1 + +# elif new_node.val < self.root.val: +# if self.root.left: +# self._find_home(new_node, self.root.left) +# if new_node.depth > self.left_depth: +# self.left_depth = new_node.depth +# self.tree_size += 1 + +# else: +# new_node.parent = self.root +# self.root.left = new_node +# self.root.left.depth = 1 +# if self.root.left.depth > self.left_depth: +# self.left_depth = self.root.left.depth +# self.tree_size += 1 +# else: +# self.root = new_node +# self.tree_size += 1 + +# def _find_home(self, node_to_add, node_to_check): +# """. +# Check if the node_to_add belongs on the left or right +# of the node_to_check, then place it there if that spot is empty, +# otherwise recur. +# """ +# if node_to_add.val > node_to_check.val: +# if node_to_check.right: +# self._find_home(node_to_add, node_to_check.right) +# else: +# node_to_add.parent = node_to_check +# node_to_check.right = node_to_add +# node_to_check.right.depth = node_to_check.depth + 1 +# # self._rebalance(node_to_add.parent) + +# elif node_to_add.val < node_to_check.val: +# if node_to_check.left: +# self._find_home(node_to_add, node_to_check.left) +# else: +# node_to_add.parent = node_to_check +# node_to_check.left = node_to_add +# node_to_check.left.depth = node_to_check.depth + 1 +# # self._rebalance(node_to_add.parent) + +# def search(self, value): +# """If a value is in the BST, return its node.""" +# return self._check_for_equivalence(value, self.root) + +# def contains(self, value): +# """Return whether or not a value is in the BST.""" +# return bool(self.search(value)) + +# def _check_for_equivalence(self, value, node_to_check): +# """. +# Check if the value matches that of the node_to_check +# if it does, return the node. If it doesn't, go left or right +# as appropriate and recur. If you reach a dead end, return None. +# """ +# try: +# if value == node_to_check.val: +# return node_to_check + +# except AttributeError: +# return None + +# if value > node_to_check.val and node_to_check.right: +# return self._check_for_equivalence(value, node_to_check.right) + +# elif value < node_to_check.val and node_to_check.left: +# return self._check_for_equivalence(value, node_to_check.left) + +# def depth(self, starting_node=None): +# """Return the depth of the tree or subtree.""" +# if not starting_node: +# if self.left_depth > self.right_depth: +# return self.left_depth +# return self.right_depth + +# def in_order(self): +# """Return a generator to perform an in-order traversal.""" +# self.visited = [] + +# if self.root is None: +# raise IndexError("Tree is empty!") + +# gen = self._in_order_gen() +# return gen + +# def _in_order_gen(self): +# """Recursive helper method for in-order traversal.""" +# current = self.root + +# while len(self.visited) < self.tree_size: +# if current.left: +# if current.left.val not in self.visited: +# current = current.left +# continue + +# if current.val not in self.visited: +# self.visited.append(current.val) +# yield current.val + +# if current.right: +# if current.right.val not in self.visited: +# current = current.right +# continue + +# current = current.parent + +# def pre_order(self): +# """Return a generator to perform an pre-order traversal.""" +# self.visited = [] + +# if self.root is None: +# raise IndexError("Tree is empty!") + +# gen = self._pre_order_gen() +# return gen + +# def _pre_order_gen(self): +# """Recursive helper method for pre-order traversal.""" +# current = self.root + +# while len(self.visited) < self.tree_size: +# if current.val not in self.visited: +# self.visited.append(current.val) +# yield current.val + +# if current.left: +# if current.left.val not in self.visited: +# current = current.left +# continue + +# if current.right: +# if current.right.val not in self.visited: +# current = current.right +# continue + +# current = current.parent + +# def post_order(self): +# """Return a generator to perform an post-order traversal.""" +# self.visited = [] + +# if self.root is None: +# raise IndexError("Tree is empty!") + +# gen = self._post_order_gen() +# return gen + +# def _post_order_gen(self): +# """Recursive helper method for post-order traversal.""" +# current = self.root + +# while len(self.visited) < self.tree_size: +# if current.left: +# if current.left.val not in self.visited: +# current = current.left +# continue + +# if current.right: +# if current.right.val not in self.visited: +# current = current.right +# continue + +# if current.val not in self.visited: +# self.visited.append(current.val) +# yield current.val + +# current = current.parent + +# def breadth_first(self): +# """Return a generator to perform a breadth-first traversal.""" +# self.visited = [] + +# if self.root is None: +# raise IndexError("Tree is empty!") + +# gen = self._breadth_first_gen(self.root) +# return gen + +# def _breadth_first_gen(self, root_node): +# """Helper generator for breadth-first traversal.""" +# queue = [self.root] +# while queue: +# current = queue[0] +# yield current.val +# queue = queue[1:] + +# if current not in self.visited: +# self.visited.append(current) + +# if current.left: +# if current.left not in self.visited: +# queue.append(current.left) + +# if current.right: +# if current.right not in self.visited: +# queue.append(current.right) + +# def delete(self, val): +# """Delete the node with the given value from the tree.""" +# node = self.search(val) +# if node is None: +# return + +# self.tree_size -= 1 +# is_root = node == self.root +# first_move_right = val > self.root.val +# last_move_right = node == node.parent.right + +# if node.left is None and node.right is None: +# if is_root: +# self.root = None +# return +# if last_move_right: +# node.parent.right = None +# # self._rebalance(node.parent) +# return +# node.parent.left = None +# # self._rebalance(node.parent) +# return + +# if node.left is None: +# if is_root: +# node.right.parent = None +# self.root = node.right +# self.root.depth = 0 +# self.right_depth = self._reassess_depths(self.root.right) +# return +# if last_move_right: +# node.parent.right = node.right +# node.right.parent = node.parent + +# else: +# node.parent.left = node.right +# node.right.parent = node.parent + +# if first_move_right: +# self.right_depth = self._reassess_depths(node.right) +# # self._rebalance(node.parent) +# return +# self.left_depth = self._reassess_depths(node.right) +# # self._rebalance(node.parent) +# return + +# if node.right is None: +# if is_root: +# node.left.parent = None +# self.root = node.left +# self.root.depth = 0 +# self.left_depth = self._reassess_depths(self.root.left) +# return +# if last_move_right: +# node.parent.right = node.left +# node.left.parent = node.parent + +# node.parent.left = node.left +# node.left.parent = node.parent +# if first_move_right: +# self.right_depth = self._reassess_depths(node.left) +# # self._rebalance(node.parent) +# return +# self.left_depth = self._reassess_depths(node.left) +# # self._rebalance(node.parent) +# return +# else: +# replacement_node = self._locate_replacement_node(node) +# self.delete(replacement_node.val) + +# if is_root: +# self.root = replacement_node +# replacement_node.parent = None +# replacement_node.depth = 0 + +# else: +# replacement_node.parent = node.parent +# replacement_node.depth = node.depth + +# replacement_node.left = node.left +# replacement_node.right = node.right +# node.left.parent = replacement_node +# node.right.parent = replacement_node +# if first_move_right or is_root: +# self.right_depth = self._reassess_depths(self.root.right) + +# else: +# self.left_depth = self._reassess_depths(self.root.left) + +# # if node.parent: +# # self._rebalance(node.parent) +# return + +# def _reassess_depths(self, starting_node): +# """Fix the depth of nodes below the starting_node an return the max_depth.""" +# self.max_depth = 0 + +# if starting_node: +# self.visited = [] +# queue = [starting_node] +# while queue: +# current = queue[0] +# current.depth = current.parent.depth + 1 +# if current.depth > self.max_depth: +# self.max_depth = current.depth +# queue = queue[1:] + +# if current not in self.visited: +# self.visited.append(current) + +# if current.left: +# if current.left not in self.visited: +# queue.append(current.left) + +# if current.right: +# if current.right not in self.visited: +# queue.append(current.right) + +# return self.max_depth - starting_node.parent.depth + +# def _locate_replacement_node(self, starting_node): +# """Return the lowest-valued node on the right side of the sub-tree.""" +# current = starting_node.right +# while current.left: +# current = current.left +# return current + +# def _rebalance(self, node): +# """Rebalance a subtree, and if its root has a parent, recur on it.""" +# node_balance = self.balance(node) + +# if node_balance == 2: +# child_balance = self.balance(node.right) + +# if child_balance == 1: +# self._rotate_left(node) + +# if child_balance == -1: +# self._rotate_right(node.right) +# self._rotate_left(node) + +# if node_balance == -2: +# child_balance = self.balance(node.left) + +# if child_balance == 1: +# self._rotate_left(node.left) +# self._rotate_right(node) + +# if child_balance == -1: +# self._rotate_right(node) + +# # if node.parent: +# # self._rebalance(node.parent) + +# def _rotate_left(self, node): +# """Rotate a node leftwards around its right child.""" +# pivot_node = node.right + +# if node.parent: +# node.parent.left = pivot_node + +# else: +# self.root = pivot_node + +# pivot_node.parent = node.parent +# node.parent = pivot_node + +# if pivot_node.left: +# pivot_node.left.parent = node + +# node.right = pivot_node.left +# pivot_node.left = node + +# def _rotate_right(self, node): +# """Rotate a node rightwards around its left child.""" +# pivot_node = node.left + +# if node.parent: +# node.parent.right = pivot_node + +# else: +# self.root = pivot_node + +# pivot_node.parent = node.parent +# node.parent = pivot_node + +# if pivot_node.right: +# pivot_node.right.parent = node + +# node.left = pivot_node.right +# pivot_node.right = node + + +# if __name__ == '__main__': # pragma: no cover +# left_bigger = BST([6, 5, 4, 3, 2, 1]) +# right_bigger = BST([1, 2, 3, 4, 5, 6]) +# bal_tree = BST([20, 12, 10, 1, 11, 16, 30, 42, 28, 27]) + +# left_bigger = time.timeit("left_bigger.search(5)", setup="from __main__ import left_bigger") +# right_bigger = time.timeit("right_bigger.search(5)", setup="from __main__ import right_bigger") +# bal_tree = time.timeit("bal_tree.search(8)", setup="from __main__ import bal_tree") + +# print('Left-Skewed Search Time: ', left_bigger) +# print('Right-Skewed Search Time: ', right_bigger) +# print('Balanced Search Time: ', bal_tree)"""Implementation of a binary search tree data structure.""" class Node(object): @@ -23,6 +502,7 @@ def __init__(self, starting_values=None): self.left_depth = 0 self.right_depth = 0 self.visited = [] + self.max_depth_reached = 0 if starting_values is None: self.root = None @@ -38,20 +518,20 @@ def __init__(self, starting_values=None): are valid parameters!') def balance(self, starting_node=None): - """Return the current balance of a tree or subtree.""" - if not starting_node: - return self.right_depth - self.left_depth + """Return the current balance of a tree or subtree.""" + if not starting_node: + return self.right_depth - self.left_depth - r_depth = 0 - l_depth = 0 + r_depth = 0 + l_depth = 0 - if starting_node.right: - r_depth += self._reassess_depths(starting_node.right) + if starting_node.right: + r_depth += self._reassess_depths(starting_node.right) - if starting_node.left: - l_depth += self._reassess_depths(starting_node.left) + if starting_node.left: + l_depth += self._reassess_depths(starting_node.left) - return r_depth - l_depth + return r_depth - l_depth def size(self): """Return the current size of the BST.""" @@ -60,6 +540,7 @@ def size(self): def insert(self, value): """Insert a new node into the BST, and adjust the balance.""" new_node = Node(value) + self.tree_size += 1 if self.root: if new_node.val > self.root.val: @@ -67,32 +548,27 @@ def insert(self, value): self._find_home(new_node, self.root.right) if new_node.depth > self.right_depth: self.right_depth = new_node.depth - self.tree_size += 1 else: new_node.parent = self.root self.root.right = new_node self.root.right.depth = 1 if self.root.right.depth > self.right_depth: self.right_depth = self.root.right.depth - self.tree_size += 1 elif new_node.val < self.root.val: if self.root.left: self._find_home(new_node, self.root.left) if new_node.depth > self.left_depth: self.left_depth = new_node.depth - self.tree_size += 1 - else: new_node.parent = self.root self.root.left = new_node self.root.left.depth = 1 if self.root.left.depth > self.left_depth: self.left_depth = self.root.left.depth - self.tree_size += 1 + else: self.root = new_node - self.tree_size += 1 def _find_home(self, node_to_add, node_to_check): """. @@ -283,7 +759,10 @@ def delete(self, val): self.tree_size -= 1 is_root = node == self.root first_move_right = val > self.root.val - last_move_right = node == node.parent.right + if node.parent: + last_move_right = node == node.parent.right + else: + last_move_right = False if node.left is None and node.right is None: if is_root: @@ -468,14 +947,16 @@ def _rotate_right(self, node): if __name__ == '__main__': # pragma: no cover - left_bigger = BST([6, 5, 4, 3, 2, 1]) - right_bigger = BST([1, 2, 3, 4, 5, 6]) - bal_tree = BST([20, 12, 10, 1, 11, 16, 30, 42, 28, 27]) + import timeit as time + + l_imba = BST([6, 5, 4, 3, 2, 1]) + r_imba = BST([1, 2, 3, 4, 5, 6]) + sample_tree = BST([20, 12, 10, 1, 11, 16, 30, 42, 28, 27]) - left_bigger = time.timeit("left_bigger.search(5)", setup="from __main__ import left_bigger") - right_bigger = time.timeit("right_bigger.search(5)", setup="from __main__ import right_bigger") - bal_tree = time.timeit("bal_tree.search(8)", setup="from __main__ import bal_tree") + l_imba = time.timeit("l_imba.search(5)", setup="from __main__ import l_imba") + r_imba = time.timeit("r_imba.search(5)", setup="from __main__ import r_imba") + sample_tree = time.timeit("sample_tree.search(8)", setup="from __main__ import sample_tree") - print('Left-Skewed Search Time: ', left_bigger) - print('Right-Skewed Search Time: ', right_bigger) - print('Balanced Search Time: ', bal_tree) + print('Left-Skewed Search Time: ', l_imba) + print('Right-Skewed Search Time: ', r_imba) + print('Balanced Search Time: ', sample_tree) diff --git a/src/test_bst.py b/src/test_bst.py index 475e845..eff0a0c 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -138,15 +138,6 @@ def test_that_bst_doesnt_work_with_non_iterable(): BST({0: 0, 1: 1, 2: 2}) -def test_adding_preexisting_node_is_not_added(sample_bst): - """Test that adding a node val that exists does not increase BST.""" - assert sample_bst.size() == 0 - sample_bst.insert(5) - sample_bst.insert(5) - sample_bst.insert(5) - assert sample_bst.size() == 1 - - def test_that_negative_numbers_work_with_insert(sample_bst): """Test that negative numbers are covered in insert.""" sample_bst.insert(-500) @@ -368,3 +359,48 @@ def test_delete_node_that_doesnt_exist(): tree = BST([5, 3, 2, 15, 44, 100]) assert tree.delete(20) is None + +def test_delete_node_that_does_exist(sample_bst): + """Test deleting node, basic functionality.""" + sample_bst.insert(20) + sample_bst.insert(300000) + sample_bst.insert(300) + assert sample_bst.contains(300) + sample_bst.delete(300) + assert sample_bst.contains(300) is False + + +def test_deleting_node_reduces_tree_size(sample_bst): + """Test size and delete together.""" + sample_bst.insert(2) + sample_bst.insert(3) + assert sample_bst.size() == 2 + sample_bst.delete(3) + assert sample_bst.size() == 1 + + +def test_delete_left_imba(): + """Test that delete works on a left-imbalanced tree.""" + tree = BST(LEFT_IMBALANCED) + tree.delete(6) + assert tree.size() == 5 and tree.root.val == 5 + tree.delete(1) + assert tree.size() == 4 and tree.root.val == 5 + + +def test_delete_right_imba(): + """Test that delete works on a right-imbalanced tree.""" + tree = BST(RIGHT_IMBALANCED) + tree.delete(6) + assert tree.size() == 5 and tree.root.val == 1 + tree.delete(1) + assert tree.size() == 4 and tree.root.val == 2 + + +def test_delete_on_sample_tree(): + """Test that delete works on a right-imbalanced tree.""" + tree = BST(SAMPLE_TREE) + tree.delete(16) + assert tree.size() == 9 and tree.root.val == 20 + tree.delete(28) + assert tree.size() == 8 and tree.root.val == 20 From def40da0e85425118f8e94b3245d258f4c961ef8 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 17:27:20 -0800 Subject: [PATCH 34/35] deleting corpse code. --- src/bst.py | 482 +---------------------------------------------------- 1 file changed, 1 insertion(+), 481 deletions(-) diff --git a/src/bst.py b/src/bst.py index 8e24e3e..8b3d7eb 100644 --- a/src/bst.py +++ b/src/bst.py @@ -1,484 +1,4 @@ -# """Implementation of a binary search tree data structure.""" -# import timeit as time - - -# class Node(object): -# """Define the Node-class object.""" - -# def __init__(self, value, left=None, right=None, parent=None): -# """Constructor for the Node class.""" -# self.val = value -# self.left = left -# self.right = right -# self.parent = parent -# self.depth = 0 - - -# class BST(object): -# """Define the BST-class object.""" - -# def __init__(self, starting_values=None): -# """Constructor for the BST class.""" -# self.tree_size = 0 -# self.left_depth = 0 -# self.right_depth = 0 -# self.visited = [] - -# if starting_values is None: -# self.root = None - -# elif isinstance(starting_values, (list, str, tuple)): -# self.root = Node(starting_values[0]) -# self.tree_size += 1 -# for i in range(len(starting_values) - 1): -# self.insert(starting_values[i + 1]) - -# else: -# raise TypeError('Only iterables or None\ -# are valid parameters!') - -# def balance(self, starting_node=None): -# """Return the current balance of a tree or subtree.""" -# if not starting_node: -# return self.right_depth - self.left_depth - -# r_depth = 0 -# l_depth = 0 - -# if starting_node.right: -# r_depth += self._reassess_depths(starting_node.right) - -# if starting_node.left: -# l_depth += self._reassess_depths(starting_node.left) - -# return r_depth - l_depth - -# def size(self): -# """Return the current size of the BST.""" -# return self.tree_size - -# def insert(self, value): -# """Insert a new node into the BST, and adjust the balance.""" -# new_node = Node(value) - -# if self.root: -# if new_node.val > self.root.val: -# if self.root.right: -# self._find_home(new_node, self.root.right) -# if new_node.depth > self.right_depth: -# self.right_depth = new_node.depth -# self.tree_size += 1 -# else: -# new_node.parent = self.root -# self.root.right = new_node -# self.root.right.depth = 1 -# if self.root.right.depth > self.right_depth: -# self.right_depth = self.root.right.depth -# self.tree_size += 1 - -# elif new_node.val < self.root.val: -# if self.root.left: -# self._find_home(new_node, self.root.left) -# if new_node.depth > self.left_depth: -# self.left_depth = new_node.depth -# self.tree_size += 1 - -# else: -# new_node.parent = self.root -# self.root.left = new_node -# self.root.left.depth = 1 -# if self.root.left.depth > self.left_depth: -# self.left_depth = self.root.left.depth -# self.tree_size += 1 -# else: -# self.root = new_node -# self.tree_size += 1 - -# def _find_home(self, node_to_add, node_to_check): -# """. -# Check if the node_to_add belongs on the left or right -# of the node_to_check, then place it there if that spot is empty, -# otherwise recur. -# """ -# if node_to_add.val > node_to_check.val: -# if node_to_check.right: -# self._find_home(node_to_add, node_to_check.right) -# else: -# node_to_add.parent = node_to_check -# node_to_check.right = node_to_add -# node_to_check.right.depth = node_to_check.depth + 1 -# # self._rebalance(node_to_add.parent) - -# elif node_to_add.val < node_to_check.val: -# if node_to_check.left: -# self._find_home(node_to_add, node_to_check.left) -# else: -# node_to_add.parent = node_to_check -# node_to_check.left = node_to_add -# node_to_check.left.depth = node_to_check.depth + 1 -# # self._rebalance(node_to_add.parent) - -# def search(self, value): -# """If a value is in the BST, return its node.""" -# return self._check_for_equivalence(value, self.root) - -# def contains(self, value): -# """Return whether or not a value is in the BST.""" -# return bool(self.search(value)) - -# def _check_for_equivalence(self, value, node_to_check): -# """. -# Check if the value matches that of the node_to_check -# if it does, return the node. If it doesn't, go left or right -# as appropriate and recur. If you reach a dead end, return None. -# """ -# try: -# if value == node_to_check.val: -# return node_to_check - -# except AttributeError: -# return None - -# if value > node_to_check.val and node_to_check.right: -# return self._check_for_equivalence(value, node_to_check.right) - -# elif value < node_to_check.val and node_to_check.left: -# return self._check_for_equivalence(value, node_to_check.left) - -# def depth(self, starting_node=None): -# """Return the depth of the tree or subtree.""" -# if not starting_node: -# if self.left_depth > self.right_depth: -# return self.left_depth -# return self.right_depth - -# def in_order(self): -# """Return a generator to perform an in-order traversal.""" -# self.visited = [] - -# if self.root is None: -# raise IndexError("Tree is empty!") - -# gen = self._in_order_gen() -# return gen - -# def _in_order_gen(self): -# """Recursive helper method for in-order traversal.""" -# current = self.root - -# while len(self.visited) < self.tree_size: -# if current.left: -# if current.left.val not in self.visited: -# current = current.left -# continue - -# if current.val not in self.visited: -# self.visited.append(current.val) -# yield current.val - -# if current.right: -# if current.right.val not in self.visited: -# current = current.right -# continue - -# current = current.parent - -# def pre_order(self): -# """Return a generator to perform an pre-order traversal.""" -# self.visited = [] - -# if self.root is None: -# raise IndexError("Tree is empty!") - -# gen = self._pre_order_gen() -# return gen - -# def _pre_order_gen(self): -# """Recursive helper method for pre-order traversal.""" -# current = self.root - -# while len(self.visited) < self.tree_size: -# if current.val not in self.visited: -# self.visited.append(current.val) -# yield current.val - -# if current.left: -# if current.left.val not in self.visited: -# current = current.left -# continue - -# if current.right: -# if current.right.val not in self.visited: -# current = current.right -# continue - -# current = current.parent - -# def post_order(self): -# """Return a generator to perform an post-order traversal.""" -# self.visited = [] - -# if self.root is None: -# raise IndexError("Tree is empty!") - -# gen = self._post_order_gen() -# return gen - -# def _post_order_gen(self): -# """Recursive helper method for post-order traversal.""" -# current = self.root - -# while len(self.visited) < self.tree_size: -# if current.left: -# if current.left.val not in self.visited: -# current = current.left -# continue - -# if current.right: -# if current.right.val not in self.visited: -# current = current.right -# continue - -# if current.val not in self.visited: -# self.visited.append(current.val) -# yield current.val - -# current = current.parent - -# def breadth_first(self): -# """Return a generator to perform a breadth-first traversal.""" -# self.visited = [] - -# if self.root is None: -# raise IndexError("Tree is empty!") - -# gen = self._breadth_first_gen(self.root) -# return gen - -# def _breadth_first_gen(self, root_node): -# """Helper generator for breadth-first traversal.""" -# queue = [self.root] -# while queue: -# current = queue[0] -# yield current.val -# queue = queue[1:] - -# if current not in self.visited: -# self.visited.append(current) - -# if current.left: -# if current.left not in self.visited: -# queue.append(current.left) - -# if current.right: -# if current.right not in self.visited: -# queue.append(current.right) - -# def delete(self, val): -# """Delete the node with the given value from the tree.""" -# node = self.search(val) -# if node is None: -# return - -# self.tree_size -= 1 -# is_root = node == self.root -# first_move_right = val > self.root.val -# last_move_right = node == node.parent.right - -# if node.left is None and node.right is None: -# if is_root: -# self.root = None -# return -# if last_move_right: -# node.parent.right = None -# # self._rebalance(node.parent) -# return -# node.parent.left = None -# # self._rebalance(node.parent) -# return - -# if node.left is None: -# if is_root: -# node.right.parent = None -# self.root = node.right -# self.root.depth = 0 -# self.right_depth = self._reassess_depths(self.root.right) -# return -# if last_move_right: -# node.parent.right = node.right -# node.right.parent = node.parent - -# else: -# node.parent.left = node.right -# node.right.parent = node.parent - -# if first_move_right: -# self.right_depth = self._reassess_depths(node.right) -# # self._rebalance(node.parent) -# return -# self.left_depth = self._reassess_depths(node.right) -# # self._rebalance(node.parent) -# return - -# if node.right is None: -# if is_root: -# node.left.parent = None -# self.root = node.left -# self.root.depth = 0 -# self.left_depth = self._reassess_depths(self.root.left) -# return -# if last_move_right: -# node.parent.right = node.left -# node.left.parent = node.parent - -# node.parent.left = node.left -# node.left.parent = node.parent -# if first_move_right: -# self.right_depth = self._reassess_depths(node.left) -# # self._rebalance(node.parent) -# return -# self.left_depth = self._reassess_depths(node.left) -# # self._rebalance(node.parent) -# return -# else: -# replacement_node = self._locate_replacement_node(node) -# self.delete(replacement_node.val) - -# if is_root: -# self.root = replacement_node -# replacement_node.parent = None -# replacement_node.depth = 0 - -# else: -# replacement_node.parent = node.parent -# replacement_node.depth = node.depth - -# replacement_node.left = node.left -# replacement_node.right = node.right -# node.left.parent = replacement_node -# node.right.parent = replacement_node -# if first_move_right or is_root: -# self.right_depth = self._reassess_depths(self.root.right) - -# else: -# self.left_depth = self._reassess_depths(self.root.left) - -# # if node.parent: -# # self._rebalance(node.parent) -# return - -# def _reassess_depths(self, starting_node): -# """Fix the depth of nodes below the starting_node an return the max_depth.""" -# self.max_depth = 0 - -# if starting_node: -# self.visited = [] -# queue = [starting_node] -# while queue: -# current = queue[0] -# current.depth = current.parent.depth + 1 -# if current.depth > self.max_depth: -# self.max_depth = current.depth -# queue = queue[1:] - -# if current not in self.visited: -# self.visited.append(current) - -# if current.left: -# if current.left not in self.visited: -# queue.append(current.left) - -# if current.right: -# if current.right not in self.visited: -# queue.append(current.right) - -# return self.max_depth - starting_node.parent.depth - -# def _locate_replacement_node(self, starting_node): -# """Return the lowest-valued node on the right side of the sub-tree.""" -# current = starting_node.right -# while current.left: -# current = current.left -# return current - -# def _rebalance(self, node): -# """Rebalance a subtree, and if its root has a parent, recur on it.""" -# node_balance = self.balance(node) - -# if node_balance == 2: -# child_balance = self.balance(node.right) - -# if child_balance == 1: -# self._rotate_left(node) - -# if child_balance == -1: -# self._rotate_right(node.right) -# self._rotate_left(node) - -# if node_balance == -2: -# child_balance = self.balance(node.left) - -# if child_balance == 1: -# self._rotate_left(node.left) -# self._rotate_right(node) - -# if child_balance == -1: -# self._rotate_right(node) - -# # if node.parent: -# # self._rebalance(node.parent) - -# def _rotate_left(self, node): -# """Rotate a node leftwards around its right child.""" -# pivot_node = node.right - -# if node.parent: -# node.parent.left = pivot_node - -# else: -# self.root = pivot_node - -# pivot_node.parent = node.parent -# node.parent = pivot_node - -# if pivot_node.left: -# pivot_node.left.parent = node - -# node.right = pivot_node.left -# pivot_node.left = node - -# def _rotate_right(self, node): -# """Rotate a node rightwards around its left child.""" -# pivot_node = node.left - -# if node.parent: -# node.parent.right = pivot_node - -# else: -# self.root = pivot_node - -# pivot_node.parent = node.parent -# node.parent = pivot_node - -# if pivot_node.right: -# pivot_node.right.parent = node - -# node.left = pivot_node.right -# pivot_node.right = node - - -# if __name__ == '__main__': # pragma: no cover -# left_bigger = BST([6, 5, 4, 3, 2, 1]) -# right_bigger = BST([1, 2, 3, 4, 5, 6]) -# bal_tree = BST([20, 12, 10, 1, 11, 16, 30, 42, 28, 27]) - -# left_bigger = time.timeit("left_bigger.search(5)", setup="from __main__ import left_bigger") -# right_bigger = time.timeit("right_bigger.search(5)", setup="from __main__ import right_bigger") -# bal_tree = time.timeit("bal_tree.search(8)", setup="from __main__ import bal_tree") - -# print('Left-Skewed Search Time: ', left_bigger) -# print('Right-Skewed Search Time: ', right_bigger) -# print('Balanced Search Time: ', bal_tree)"""Implementation of a binary search tree data structure.""" +"""Implementation of Binary Search Tree data structure.""" class Node(object): From f9e47fff0308e6c06fb4d63f180737a97c0a1a79 Mon Sep 17 00:00:00 2001 From: CHELSEA DOLE Date: Sun, 3 Dec 2017 17:33:10 -0800 Subject: [PATCH 35/35] adding README info, runtime. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e9bc834..0b25c99 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,7 @@ * breadth_first() = *This BST traversal returns a generator that outputs the node values in order of their "levels". It produces first the root, then all nodes (left to right) in the first depth level, then all nodes (left to right) in the second depth level, et cetera. Like in_order, pre_order, and post_order, it has a runtime of O(n), not because you visit every node once (you visit them more than once here) but because the work you do/time you take is constant and grows constantly per node addition.* +* delete() = *This BST method deletes a node if it exists, and returns None if it does not exist. It also rebalances the BST (using the internal _rebalance() method, and resets variables for tree_size. Its time complexity is O(n), because worst case scenario the node to delete is at the very end of a imbalanced tree, and therefore it must go through every node.* + + +