Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ language: python
python:
- "2.7"
- "3.5"

install:
- pip install -e .[test]

script: py.test
script: py.test src/test_bst.py
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.org/julienawilson/data-structures.svg?branch=bst)](https://travis-ci.org/julienawilson/data-structures)

# data-structures
Patrick Saunders and Julien Wilson
<br>
Expand Down Expand Up @@ -30,3 +32,8 @@ Methods include:
Trees that are higher on the left than the right should return a positive value;
trees that are higher on the right than the left should return a negative value;
an ideally-balanced tree should return 0.
* in_order(self): Return a generator that returns each node value from in-order traversal.
* pre_order(self): Return a generator that returns each node value from pre-order traversal.
* post_order(self): Return a generator that returns each node value from post_order traversal.
* breadth_first(self): Return a generator returns each node value from breadth-first traversal.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like a method is missing...

68 changes: 65 additions & 3 deletions src/bst.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
Trees that are higher on the left than the right should return a positive value;
trees that are higher on the right than the left should return a negative value;
an ideally-balanced tree should return 0.
in_order(self): Return a generator that returns each node value from in-order traversal.
pre_order(self): Return a generator that returns each node value from pre-order traversal.
post_order(self): Return a generator that returns each node value from post_order traversal.
breadth_first(self): Return a generator returns each node value from breadth-first traversal.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like a method is missing...

"""

from queue import Queue
Expand All @@ -18,11 +23,12 @@
class Node():
"""Node object for the binary search tree."""

def __init__(self, value, left=None, right=None):
def __init__(self, value, left=None, right=None, parent=None):
"""Instantiate a node object."""
self.value = value
self.left = left
self.right = right
self.parent = parent


class BinarySearchTree():
Expand All @@ -49,13 +55,15 @@ def insert(self, value):
current_node = current_node.left
else:
current_node.left = Node(value)
current_node.left.parent = current_node
self._size += 1
break
elif value > current_node.value:
if current_node.right:
current_node = current_node.right
else:
current_node.right = Node(value)
current_node.right.parent = current_node
self._size += 1
break
else:
Expand Down Expand Up @@ -126,11 +134,11 @@ def contains(self, value):
def balance(self):
"""Return numerical representation of how balanced the tree is."""
if self.root.left:
depth_left = self.depth(self.root.left)
depth_left = self.depth(self.root.left) + 1
else:
depth_left = 0
if self.root.right:
depth_right = self.depth(self.root.right)
depth_right = self.depth(self.root.right) + 1
else:
depth_right = 0
balance = depth_right - depth_left
Expand Down Expand Up @@ -191,3 +199,57 @@ def breadth_first(self):
if current_node.right:
trav_list.enqueue(current_node.right)
yield current_node

def delete(self, value):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Delete breaks with empty tree. See assignment for how to handle this.

In [2]: bst = BinarySearchTree()

In [3]: bst.delete(1)
---------------------------------------------------------------------------
AttributeError: 'NoneType' object has no attribute 'value'

"""Get rid of a node. Or at least its connection."""
target_node = self.search(value)
if not target_node:
return None
if not (target_node.left or target_node.right):
if target_node.value > target_node.parent.value:
target_node.parent.right = None
target_node.parent = None
self._size -= 1
else:
target_node.parent.left = None
target_node.parent = None
self._size -= 1
elif not (target_node.left and target_node.right):
if target_node.left:
if target_node.value < target_node.parent.value:
target_node.left.parent = target_node.parent
target_node.parent.left = target_node.left
else:
target_node.left.parent = target_node.parent
target_node.parent.right = target_node.left
self._size -= 1
target_node.parent = None
target_node.left = None
if target_node.right:
if target_node.value < target_node.parent.value:
target_node.right.parent = target_node.parent
target_node.parent.left = target_node.right
else:
target_node.right.parent = target_node.parent
target_node.parent.right = target_node.right
self._size -= 1
target_node.parent = None
target_node.right = None
else:
del_node = self.search(value)
current_node = del_node.right
while current_node.left:
current_node = current_node.left
replace_node = current_node
self.delete(current_node)
if del_node.parent:
replace_node.parent = del_node.parent
if replace_node.value < del_node.value:
del_node.parent.left = replace_node
else:
del_node.parent.right = replace_node
replace_node.left = del_node.left
replace_node.right = del_node.right
del_node.parent = None
del_node.left = None
del_node.right = None
55 changes: 55 additions & 0 deletions src/test_bst.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ def test_contains_true_weird_tree_root(weird_tree):
assert weird_tree.contains(50) is True


def test_contains_with_nonexistent_val_gt_root(small_tree):
"""Test contains returns False when value is greater than root but node nonexistent."""
assert small_tree.contains(99) is False


def test_depth_on_small_tree(small_tree):
"""Test the size on a small Tree."""
assert small_tree.depth() == 2
Expand All @@ -213,6 +218,13 @@ def test_balance_on_weird_tree(weird_tree):
"""Test balance of smal tree fixture."""
assert weird_tree.balance() == 4

def test_balance_w_no_left_nodes():
b_tree = BinarySearchTree()
b_tree.insert(17)
b_tree.insert(43)
import pdb; pdb.set_trace()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You probably shouldn't need this on github.

assert b_tree.balance() == 1


def test_inorder_no_nodes():
"""Test in-order traversal on empty tree returns empty path."""
Expand Down Expand Up @@ -352,3 +364,46 @@ def test_bfs_weird_tree(weird_tree):
for node in weird_tree.breadth_first():
bfs_list.append(node.value)
assert bfs_list == [50, 44, 79, 2, 48, 80, 49, 83, 90, 100, 103, 102]


def test_delete_node_with_no_children(small_tree):
"""Test calling delete on node with no children."""
small_tree.delete(35)
assert small_tree.search(35) == None


def test_delete_node_with_no_children_annuls_parent_connection(small_tree):
"""Test calling delete on node with no children kills parent's connection."""
small_tree.delete(35)
assert small_tree.search(40).left is None
with pytest.raises(AttributeError):
assert small_tree.search(35).parent


def test_delete_node_with_no_children_annuls_own_connection(small_tree):
"""Test calling delete on node with no children kills parent's connection."""
small_tree.delete(35)
with pytest.raises(AttributeError):
assert small_tree.search(35).parent


def test_delete_node_with_one_child_reassigns_connections(small_tree):
"""Test deleting a node reassigns its one child to expected new parent."""
small_tree.delete(40)
assert small_tree.search(35).parent.value == 50
assert small_tree.search(50).left.value == 35


def test_delete_node_annuls_own_connections(small_tree):
"""Test calling delete on node kills parent and child connections."""
small_tree.delete(40)
with pytest.raises(AttributeError):
assert small_tree.search(40).parent is None
with pytest.raises(AttributeError):
assert small_tree.search(40).left is None


def test_delete_updates_size(small_tree):
"""Test that deleting a node updates tree's size."""
small_tree.delete(40)
assert small_tree.size() == 5