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: 3 additions & 0 deletions applications/expensive_seq/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ ends.
Hint: Va Clguba, n qvpg xrl pna or nal vzzhgnoyr glcr... vapyhqvat n
ghcyr.

In Python, a dict key can be any immutable type... including a
tuple.

(That's encrypted with ROT13--Google `rot13 decoder` to decode it if you
want the hint.)
2 changes: 1 addition & 1 deletion applications/expensive_seq/expensive_seq.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

def expensive_seq(x, y, z):
# Your code here



if __name__ == "__main__":
Expand Down
7 changes: 7 additions & 0 deletions applications/lookup_table/lookup_table.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Your code here
import random

prevV = {}

def slowfun_too_slow(x, y):
v = math.pow(x, y)
v = math.factorial(v)
v //= (x + y)
v %= 982451653

prevV[x, y] = v
return v

def slowfun(x, y):
Expand All @@ -15,7 +18,11 @@ def slowfun(x, y):
output, but completes quickly instead of taking ages to run.
"""
# Your code here
if x and y in prevV:
return prevV[x, y]

else:
slowfun_too_slow(x, y)


# Do not modify below this line!
Expand Down
2 changes: 1 addition & 1 deletion applications/no_dups/no_dups.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
def no_dups(s):
# Your code here

pass


if __name__ == "__main__":
Expand Down
10 changes: 10 additions & 0 deletions applications/word_count/word_count.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
def word_count(s):
# Your code here
counts = {}
words = s.lower()
lowerWords = words.split()

for word in lowerWords:
if word in counts:
counts[word] += 1
else:
counts[word] = 1

return counts


if __name__ == "__main__":
Expand Down
113 changes: 107 additions & 6 deletions hashtable/hashtable.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from linked_list import Node
from linked_list import LinkedList

class HashTableEntry:
"""
Linked List hash table key/value pair
Expand All @@ -7,6 +10,11 @@ def __init__(self, key, value):
self.value = value
self.next = None

def __eq__(self, other):
if isinstance(other, HashTableEntry):
return self.key == other.key
return False


# Hash table can't have fewer than this many slots
MIN_CAPACITY = 8
Expand All @@ -21,7 +29,12 @@ class HashTable:
"""

def __init__(self, capacity):
# Your code here
# self.table = [None] * capacity
# self.capacity = capacity

self.table = [None] * capacity
self.capacity = capacity
self.num_elements = 0


def get_num_slots(self):
Expand All @@ -35,6 +48,7 @@ def get_num_slots(self):
Implement this.
"""
# Your code here
return len(self.table)


def get_load_factor(self):
Expand All @@ -44,6 +58,8 @@ def get_load_factor(self):
Implement this.
"""
# Your code here
# load factor = num of elements in the hash table / num slots
return self.num_elements / self.get_num_slots()


def fnv1(self, key):
Expand All @@ -54,16 +70,18 @@ def fnv1(self, key):
"""

# Your code here



def djb2(self, key):
"""
DJB2 hash, 32-bit

Implement this, and/or FNV-1.
"""
# Your code here

hash = 5381
for x in key:
hash = ((hash << 5) + hash) + ord(x)
return hash & 0xFFFFFFFF

def hash_index(self, key):
"""
Expand All @@ -82,6 +100,37 @@ def put(self, key, value):
Implement this.
"""
# Your code here
# self.table[self.hash_index(key)] = value

# index = self.hash_index(key)
# current_entry = self.table[index]

# while current_entry is not None and current_entry.key != key:
# current_entry = current_entry.next
# if current_entry is not None:
# current_entry.value = value
# else:
# new_entry = HashTableEntry(key, value)
# new_entry.next = self.table[index]
# self.table[index] = new_entry

# self.item_count += 1
# if self.get_load_factor() > 0.7:
# self.resize(self.capacity * 2)

hash_index = self.hash_index(key)
if self.table[hash_index] != None:
linked_list = self.table[hash_index]
did_add_new_node = linked_list.insert_at_head_or_overwrite(Node(HashTableEntry(key, value)))
if did_add_new_node:
self.num_elements += 1
else:
linked_list = LinkedList()
linked_list.insert_at_head(Node(HashTableEntry(key, value)))
self.table[hash_index] = linked_list
self.num_elements += 1
if self.get_load_factor() > 0.7:
self.resize(self.get_num_slots() * 2)


def delete(self, key):
Expand All @@ -93,6 +142,21 @@ def delete(self, key):
Implement this.
"""
# Your code here
# value = self.table[self.hash_index(key)]
# if value == None:
# print('value is already None')
# self.table[self.hash_index(key)] = None

hash_index = self.hash_index(key)
if self.table[hash_index] != None:
linked_list = self.table[hash_index]
did_delete_node = linked_list.delete(HashTableEntry(key, None))
if did_delete_node != None:
self.num_elements -= 1
if self.get_load_factor() < 0.2:
self.resize(self.get_num_slots() / 2)
else:
print("Warning: node not found")


def get(self, key):
Expand All @@ -104,6 +168,23 @@ def get(self, key):
Implement this.
"""
# Your code here
# return self.table[self.hash_index(key)]
# index = self.hash_index(key)

# current_entry = self.table[index]

# while current_entry is not None:
# if(current_entry.key == key):
# return current_entry.value
# current_entry = current_entry.next

hash_index = self.hash_index(key)
if self.table[hash_index] != None:
linked_list = self.table[hash_index]
node = linked_list.find(HashTableEntry(key, None))
if node != None:
return node.value.value
return None


def resize(self, new_capacity):
Expand All @@ -113,8 +194,28 @@ def resize(self, new_capacity):

Implement this.
"""
# Your code here

old_table = self.table
self.table = [None] * int(new_capacity)
self.num_elements = 0

for element in old_table:
if element is None:
continue
curr_node = element.head
while curr_node != None:
temp = curr_node.next
curr_node.next = None
hash_index = self.hash_index(curr_node.value.key)

if self.table[hash_index] != None:
self.table[hash_index].insert_at_head(curr_node)
else:
linked_list = LinkedList()
linked_list.insert_at_head(curr_node)
self.table[hash_index] = linked_list

curr_node = temp
self.num_elements += 1


if __name__ == "__main__":
Expand Down
79 changes: 79 additions & 0 deletions hashtable/linked_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
class Node:
def __init__(self, value):
self.value = value
self.next = None

class LinkedList:
def __init__(self):
self.head = None

def __repr__(self):
currStr = ""
curr = self.head
while curr != None:
currStr += f'{str(curr.value)} ->'
curr = curr.next
return currStr

# return node w/ value
# runtime: O(n) where n = number nodes
def find(self, value):
curr = self.head
while curr != None:
if curr.value == value:
return curr
curr = curr.next
return None

# deletes node w/ given value then return that node
# runtime: O(n) where n = number of nodes
def delete(self, value):
cur = self.head

# special case if we need to delete the head
if cur.value == value:
self.head = cur.next
return cur

prev = None
cur = cur.next

while cur != None:
if cur.value == value:
prev.next = cur.next
cur.next = None
return cur
else:
prev = cur
cur = cur.next

return None

# insert node at head of list
# runtime: O(1)
def insert_at_head(self, node):
node.next = self.head
self.head = node

# overwrite node or insert node at head
# runtime: O(n)
def insert_at_head_or_overwrite(self, node):
existingNode = self.find(node.value) # O(n)
if existingNode != None:
existingNode.value = node.value
return False
else:
self.insert_at_head(node) # O(1)
return True

def add_to_tail(self, value):
# 0. create new node from value
new_node = Node(value, None)
# 1. check if list is empty
if not self.head:
# if list is empty, set both head and tail to new node
self.head = new_node
self.tail = new_node
else:
self.tail.set_next(new_node)
self.tail = new_node