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
155 changes: 141 additions & 14 deletions binary_search_tree/binary_search_tree.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from collections import deque
from queue import Queue
from stack import Stack

"""
Binary search trees are a data structure that enforce an ordering over
the data they store. That ordering in turn makes it a lot more efficient
Expand All @@ -9,6 +13,8 @@
2. Implement the `in_order_print`, `bft_print`, and `dft_print` methods
on the BSTNode class.
"""


class BSTNode:
def __init__(self, value):
self.value = value
Expand All @@ -17,37 +23,157 @@ def __init__(self, value):

# Insert the given value into the tree
def insert(self, value):
pass
# left case?
# check if the value is less than the root value?
if value < self.value:
# move to the left and check if it is none?
if self.left == None:
# insert node here
new_node = BSTNode(value)
self.left = new_node
# otherwise
else:
# call insert on the root's left node
self.left.insert(value)
# right case?
if value >= self.value:
# otherwise
# move to the right and check if it is none?
if self.right == None:
# insert the node here
new_node = BSTNode(value)
self.right = new_node
# otherwise
else:
# call insert on the root's right node
self.right.insert(value)

# Return True if the tree contains the value
# False if it does not

def contains(self, target):
pass
# check if the node is == target
if self.value == target:
# if true return true
return True
# otherwise check if target is < node value
elif target < self.value:
# if left is None, target doesn't exist in tree, return false
if self.left == None:
return False
# if left value is = target return true
elif self.left.value == target:
return True
# otherwise move down left, call contains on left node
else:
self.left.contains(target)
# otherwise check if target is >= node value
elif target > self.value:
# if right is None, target doesn't exist in tree, return false
if self.right == None:
return False
# if right value is = target return true
elif self.right.value == target:
return True
# otherwise move down right, call contains on right node
else:
self.right.contains(target)

# Return the maximum value found in the tree

def get_max(self):
pass
if self.right == None:
return self.value
else:
max_value = self.right.get_max()
return max_value

# Call the function `fn` on the value of each node
def for_each(self, fn):
pass
# run the function passing in the value of the node
fn(self.value)

# if left is not none run for each on left
if self.left:
# call function on left
self.left.for_each(fn)

# if right is not None run for each on right
if self.right:
# call function on right
self.right.for_each(fn)

# Part 2 -----------------------

# Print all the values in order from low to high
# Hint: Use a recursive, depth first traversal
def in_order_print(self):
pass
# base case
# if there are no more nodes
if self == None:
# return
return self

# if there is a node to the left
if self.left is not None:
# call in order print on the left
self.left.in_order_print()
# print the value of the current node (self.value)
print(self.value)

# if there is a node to the right
if self.right is not None:
# call order print on the right
self.right.in_order_print()

# Print the value of every node, starting with the given node,
# in an iterative breadth first traversal
def bft_print(self):
pass

def bft_print(self): # use a queue
# create a queue
values = Queue()
# eneque the first node(self)
values.enqueue(self)

# while there is data on queue
while values.size > 0:
# dequeue from queue on to current_node
self = values.dequeue()
# print the current_node's value
print(self.value)
# if the current_node has a left child
if self.left:
# enqueue the left child
values.enqueue(self.left)
# if the current_node has a right child
if self.right:
# enqueue right child
values.enqueue(self.right)
# this increases our values.size
# continuing the loop until done

# Print the value of every node, starting with the given node,
# in an iterative depth first traversal
def dft_print(self):
pass

def dft_print(self): # use a stack
# instantiate stack
values = Stack()
# push starting node
values.push(self)
# while stack is NOT empty:
while values.size > 0:
# pop the node
# print node.value
self = values.pop()
print(self.value)
# if node.left:
# push left node
if self.left:
values.push(self.left)
# if node.right:
# push right node
if self.right:
values.push(self.right)

# Stretch Goals -------------------------
# Note: Research may be required
Expand All @@ -60,6 +186,7 @@ def pre_order_dft(self):
def post_order_dft(self):
pass


"""
This code is necessary for testing the `print` methods
"""
Expand All @@ -77,9 +204,9 @@ def post_order_dft(self):
bst.dft_print()

print("elegant methods")
print("pre order")
bst.pre_order_dft()
# print("pre order")
# bst.pre_order_dft()
print("in order")
bst.in_order_dft()
print("post order")
bst.post_order_dft()
bst.in_order_print()
# print("post order")
# bst.post_order_dft()
36 changes: 36 additions & 0 deletions binary_search_tree/queue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""
A queue is a data structure whose primary purpose is to store and
return elements in First In First Out order.

1. Implement the Queue class using an array as the underlying storage structure.
Make sure the Queue tests pass.
2. Re-implement the Queue class, this time using the linked list implementation
as the underlying storage structure.
Make sure the Queue tests pass.
3. What is the difference between using an array vs. a linked list when
implementing a Queue?

Stretch: What if you could only use instances of your Stack class to implement the Queue?
What would that look like? How many Stacks would you need? Try it!
"""


class Queue:
def __init__(self):
self.size = 0
# self.storage = []
self.storage = [] # instead of LinkedList()

def __len__(self):
return self.size

def enqueue(self, value):
self.size += 1
return self.storage.append(value) # intead of add_to_tail

def dequeue(self):
if self.size == 0:
return None
self.size -= 1
# return self.storage.pop(0)
return self.storage.pop(0) # intead of remove_tail
31 changes: 31 additions & 0 deletions binary_search_tree/stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
A stack is a data structure whose primary purpose is to store and
return elements in Last In First Out order.

1. Implement the Stack class using an array as the underlying storage structure.
Make sure the Stack tests pass.
2. Re-implement the Stack class, this time using the linked list implementation
as the underlying storage structure.
Make sure the Stack tests pass.
3. What is the difference between using an array vs. a linked list when
implementing a Stack?
"""


class Stack:
def __init__(self):
self.size = 0
self.storage = []

def __len__(self):
return self.size

def push(self, value):
self.size += 1
self.storage.append(value)

def pop(self):
if self.size == 0:
return None
self.size -= 1
return self.storage.pop()
22 changes: 12 additions & 10 deletions binary_search_tree/test_binary_search_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io
from binary_search_tree import BSTNode


class BinarySearchTreeTests(unittest.TestCase):
def setUp(self):
self.bst = BSTNode(5)
Expand All @@ -15,7 +16,7 @@ def test_insert(self):
self.bst.insert(6)
self.assertEqual(self.bst.left.right.value, 3)
self.assertEqual(self.bst.right.left.value, 6)

def test_handle_dupe_insert(self):
self.bst2 = BSTNode(1)
self.bst2.insert(1)
Expand All @@ -38,7 +39,7 @@ def test_get_max(self):

def test_for_each(self):
arr = []
cb = lambda x: arr.append(x)
def cb(x): return arr.append(x)

v1 = random.randint(1, 101)
v2 = random.randint(1, 101)
Expand Down Expand Up @@ -94,17 +95,18 @@ def test_print_traversals(self):
self.assertTrue(output == "1\n8\n5\n7\n6\n3\n4\n2\n" or
output == "1\n8\n5\n3\n2\n4\n7\n6\n")

sys.stdout = io.StringIO()
self.bst.pre_order_dft()
output = sys.stdout.getvalue()
self.assertEqual(output, "1\n8\n5\n3\n2\n4\n7\n6\n")
# sys.stdout = io.StringIO()
# self.bst.pre_order_dft()
# output = sys.stdout.getvalue()
# self.assertEqual(output, "1\n8\n5\n3\n2\n4\n7\n6\n")

sys.stdout = io.StringIO()
self.bst.post_order_dft()
output = sys.stdout.getvalue()
self.assertEqual(output, "2\n4\n3\n6\n7\n5\n8\n1\n")
# sys.stdout = io.StringIO()
# self.bst.post_order_dft()
# output = sys.stdout.getvalue()
# self.assertEqual(output, "2\n4\n3\n6\n7\n5\n8\n1\n")

sys.stdout = stdout_ # Restore stdout


if __name__ == '__main__':
unittest.main()
Loading