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
2 changes: 1 addition & 1 deletion Data_Structures_Questions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Answer the following questions for each of the data structures you implemented a

## Stack

1. What is the runtime complexity of `push` using a list?
1. What is the runtime complexity of `push` using a list?

2. What is the runtime complexity of `push` using a linked list?

Expand Down
108 changes: 94 additions & 14 deletions binary_search_tree/binary_search_tree.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""
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
at searching for a particular piece of data in the tree.

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
at searching for a particular piece of data in the tree.
This part of the project comprises two days:
1. Implement the methods `insert`, `contains`, `get_max`, and `for_each`
on the BSTNode class.
2. Implement the `in_order_print`, `bft_print`, and `dft_print` methods
on the BSTNode class.
"""
from queue import Queue


class BSTNode:
def __init__(self, value):
self.value = value
Expand All @@ -17,20 +19,63 @@ def __init__(self, value):

# Insert the given value into the tree
def insert(self, value):
pass

# Empty tree use case
if (self.left is None) & (self.right is None):
if value >= self.value:
self.right = BSTNode(value)
else:
self.left = BSTNode(value)
elif (value < self.value):
# value goes to left branch of root
if self.left is None:
self.left = BSTNode(value)
else:
self.left.insert(value)
else:
# Value goes to the right branch of root
if self.right is None:
self.right = BSTNode(value)
else:
self.right.insert(value)
# Return True if the tree contains the value
# False if it does not

def contains(self, target):
pass
if self.value == target:
return True
elif target < self.value:
if self.left is None:
return False
else:
return self.left.contains(target)
else:
if self.right is None:
return False
else:
return self.right.contains(target)

# Return the maximum value found in the tree
def get_max(self):
pass
# go right until you cannot anymore
# return value
if self.right is None:
return self.value
else:
self.right.get_max()

# Call the function `fn` on the value of each node
def for_each(self, fn):
pass
fn(self.value)
# base case - no children
if self.left is None and self.right is None:
return
# recursive case - 1 or more children
# go left, call fn(value) for each node
if self.left:
self.left.for_each(fn)
# go right, call fn(value) for each node
if self.right:
self.right.for_each(fn)

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

Expand All @@ -42,12 +87,47 @@ def in_order_print(self):
# Print the value of every node, starting with the given node,
# in an iterative breadth first traversal
def bft_print(self):
pass
# instantiate a Queue
q = Queue()
# insert the value
q.enqueue(self)

# while length of q is greater than 0
while q.size > 0:
# pop off the top
top = q.dequeue()
# print it
print(top.value)
# if there is a left child
if top.left:
# add left child to queue
q.enqueue(top.left)
# if there is a right child
if top.right:
# add right child to queue
q.enqueue(top.right)

# Print the value of every node, starting with the given node,
# in an iterative depth first traversal
def dft_print(self):
pass
# create a stack to keep track of nodes we are processing
# push self into stack
# if a tree exists
if self:
# print the current value (as it's the first traversal)
print(self.value)
# if there is a left child
if self.left:
# re-run function with left child as root of tree
self.left.dft_print()
# if there is a right child
if self.right:
# re-run function with right child as root of tree
self.right.dft_print()
# while something still in the stack (not done processing all nodes)
# use existing 'for_each' as a reference for the traversal logic
# push when we START, pop when a node is DONE
# and don't forget to call print()

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

"""
"""
This code is necessary for testing the `print` methods
"""
"""
bst = BSTNode(1)

bst.insert(8)
Expand All @@ -82,4 +162,4 @@ def post_order_dft(self):
print("in order")
bst.in_order_dft()
print("post order")
bst.post_order_dft()
bst.post_order_dft()
120 changes: 105 additions & 15 deletions doubly_linked_list/doubly_linked_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
as well as its next node in the List.
"""
class ListNode:
def __init__(self, value, prev=None, next=None):
self.prev = prev
def __init__(self, value, prev_node=None, next_node=None):
self.prev = prev_node
self.value = value
self.next = next
self.next = next_node

def delete(self):
if self.prev:
self.prev.next = self.next
if self.next:
self.next.prev = self.prev

"""
Our doubly-linked list class. It holds references to
Expand All @@ -27,56 +33,140 @@ def __len__(self):
the old head node's previous pointer accordingly.
"""
def add_to_head(self, value):
pass

# create new_node
new_node = ListNode(value)
# 1. add to empty
if self.head is None:
self.head = new_node
self.tail = new_node
# 2. add to nonempty
else:
new_node.next = self.head
self.head.prev = new_node
self.head = new_node
# update length
self.length += 1

"""
Removes the List's current head node, making the
current head's next node the new head of the List.
Returns the value of the removed Node.
"""
def remove_from_head(self):
pass

# save value to return
value = self.head.value
self.delete(self.head)
return value

"""
Wraps the given value in a ListNode and inserts it
as the new tail of the list. Don't forget to handle
the old tail node's next pointer accordingly.
"""
def add_to_tail(self, value):
pass

new_node = ListNode(value)
# empty list
if self.head is None and self.tail is None:
# set new_node to head and tail
self.head = new_node
self.tail = new_node
else:
# have the current tail's 'next' pointing to the new node
self.tail.next = new_node
# then set the new node to now be the tail
self.tail = new_node
self.length += 1

"""
Removes the List's current tail node, making the
current tail's previous node the new tail of the List.
Returns the value of the removed Node.
"""
def remove_from_tail(self):
pass
self.length -= 1
# empty list
if self.head is None and self.tail is None:
return
else:
# store tail value before removal
value = self.tail.value
# 1 element
if self.tail == self.head:
self.tail = None
self.head = None
self.length = 0
return value
self.tail = self.tail.prev
if self.tail:
self.tail.next = None
self.length -= 1
return value

"""
Removes the input node from its current spot in the
List and inserts it as the new head node of the List.
"""
def move_to_front(self, node):
pass

# 1. delete()
if node is self.head:
return
self.delete(node)
# 2. add_to_head()
self.add_to_head(node.value)


"""
Removes the input node from its current spot in the
List and inserts it as the new tail node of the List.
"""
def move_to_end(self, node):
pass
value = node.value
if self.head == node:
self.head = self.head.next
self.head.prev = None
node.delete()
self.length -= 1
self.add_to_tail(value)

"""
Deletes the input node from the List, preserving the
order of the other elements of the List.
"""
def delete(self, node):
pass
# don't need to return value
# DO need to update head and tail
if self.head is None:
return None
elif self.head is self.tail:
self.head = None
self.tail = None
elif node is self.head: # list has +2 nodes
self.head = node.next
node.delete() # updating prev and/or next pointers
elif node is self.tail:
self.tail = node.prev
node.delete()
else:
node.delete()

self.length -= 1

"""
Finds and returns the maximum value of all the nodes
in the List.
"""
def get_max(self):
pass
# empty list
if self.head is None:
return None
# keep track of current node
# keep track of max
cur_node = self.head
max_value = self.head.value
# loop through DLL
while cur_node: # same as saying is not none
# comparing with cur_max
if cur_node.value > max_value:
max_value = cur_node.value
cur_node = cur_node.next
return max_value
15 changes: 11 additions & 4 deletions queue/queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,20 @@
class Queue:
def __init__(self):
self.size = 0
# self.storage = ?
self.storage = []


def __len__(self):
pass
return self.size

def enqueue(self, value):
pass
self.storage.insert(0, value)
self.size += 1

def dequeue(self):
pass
if self.size == 0:
return None
else:
self.size -= 1
return self.storage.pop()
# first in first out
Loading