Skip to content
Open

Day 3 #194

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
13 changes: 11 additions & 2 deletions applications/expensive_seq/expensive_seq.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
# Your code here

dictionary = {}

def expensive_seq(x, y, z):
# Your code here
if (x, y, z) in dictionary:
return dictionary[(x, y, z)]

result = 0
if x <= 0:
result = y + z
if x > 0:
result = expensive_seq(x-1,y+1,z) + expensive_seq(x-2,y+2,z*2) + expensive_seq(x-3,y+3,z*3)

dictionary[(x, y, z)] = result
return result


if __name__ == "__main__":
Expand Down
26 changes: 17 additions & 9 deletions applications/lookup_table/lookup_table.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
# Your code here
import math
import random

dictionary = {}

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

return v
# return v

def slowfun(x, y):
"""
Rewrite slowfun_too_slow() in here so that the program produces the same
output, but completes quickly instead of taking ages to run.
"""
# Your code here

if (x, y) in dictionary:
return dictionary[(x, y)]
v = math.pow(x, y)
v = math.factorial(v)
v //= (x + y)
v %= 982451653

dictionary[(x, y)] = v
return v

# Do not modify below this line!

Expand Down
17 changes: 16 additions & 1 deletion applications/no_dups/no_dups.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@

def no_dups(s):
# Your code here
array = []
result_string = ""
split_string = s.split()

if s == "":
return result_string

for word in split_string:
if word not in array:
array.append(word)
result_string += word + " "

if result_string.endswith(" "):
return result_string[:-1]

return result_string


if __name__ == "__main__":
Expand Down
18 changes: 17 additions & 1 deletion applications/word_count/word_count.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
def word_count(s):
# Your code here
dictionary = {}
special_characters = ['"', ":", ";", ",", ".", "-", "+", "=", "/", "|", "[", "]", "{", "}", "(", ")", "*", "^", "&", "\\"]

lower_case = s.lower()

for i in special_characters:
lower_case = lower_case.replace(i, "")

split_string = lower_case.split()
if split_string.count == 0:
return dictionary

for word in split_string:
if word in dictionary:
dictionary[word] += 1
else:
dictionary[word] = 1
return dictionary


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

class HashTableEntry:
"""
Linked List hash table key/value pair
"""
def __init__(self, key, value):
self.key = key
self.value = value
self.next = None

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


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

def find(self, value):
# return node with value
curr = self.head

#walk through the LL and check the value
while curr != None:
if curr.value == value:
return curr
curr = curr.next

return None

def delete(self, value):
curr = self.head

# special case for remove head
if curr.value == value:
self.head = curr.next
curr.next = None
return curr

prev = None

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

return None

# insert node at head of list
def add_to_head(self, node):
node.next = self.head
self.head = node

# overwrite node or insert node at head
def insert_at_head_or_overwrite(self, node):
existing_node = self.find(node.value)
if existing_node != None:
existing_node.value = node.value
return False
else:
self.add_to_head(node)
return True

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

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


def get_num_slots(self):
Expand All @@ -34,86 +103,112 @@ def get_num_slots(self):

Implement this.
"""
# Your code here

return len(self.table)

def get_load_factor(self):
"""
Return the load factor for this hash table.

Implement this.
"""
# Your code here
return self.count / self.get_num_slots()


def fnv1(self, key):
"""
FNV-1 Hash, 64-bit

Implement this, and/or DJB2.
"""

# Your code here
pass


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

Implement this, and/or FNV-1.
"""
# Your code here
hash = 5381
for s in key:
hash = ((hash << 5) + hash) + ord(s)
return hash & 0xFFFFFFFF


def hash_index(self, key):
"""
Take an arbitrary key and return a valid integer index
between within the storage capacity of the hash table.
"""
#return self.fnv1(key) % self.capacity
return self.djb2(key) % self.capacity

def put(self, key, value):
"""
Store the value with the given key.

Hash collisions should be handled with Linked List Chaining.

Implement this.
"""
# Your code here
# hash the key and get the index
index = self.hash_index(key)
# check if the table at index is empty
if self.table[index] == None:
# if it is -> initialize new LinkedList
ll = LinkedList()
ll.add_to_head(Node(HashTableEntry(key, value)))
self.table[index] = ll
# increase count
self.count += 1
else:
# if table at index already has a LL, get reference to current LL
curr_ll: LinkedList = self.table[index]
# set the new Node as the head
did_add_new_node = curr_ll.insert_at_head_or_overwrite(Node(HashTableEntry(key, value)))
if did_add_new_node:
# increase count
self.count += 1

if self.get_load_factor() > 0.7:
self.resize(self.get_num_slots() * 2)




def delete(self, key):
"""
Remove the value stored with the given key.

Print a warning if the key is not found.

Implement this.
"""
# Your code here
index = self.hash_index(key)
if self.table[index] != None:
ll: LinkedList = self.table[index]
did_delete_node = ll.delete(HashTableEntry(key, None))
if did_delete_node != None:
self.count -= 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):
"""
Retrieve the value stored with the given key.

Returns None if the key is not found.
index = self.hash_index(key)
if self.table[index] != None:
ll: LinkedList = self.table[index]
node = ll.find(HashTableEntry(key, None))
if node != None:
return node.value.value
return None

Implement this.
"""
# Your code here


def resize(self, new_capacity):
"""
Changes the capacity of the hash table and
rehashes all key/value pairs.

Implement this.
"""
# Your code here
old_table = self.table
self.table = [None] * int(new_capacity)
self.count = 0
self.capacity = new_capacity

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

if self.table[index] != None:
self.table[index].add_to_head(curr_node)
else:
ll = LinkedList()
ll.add_to_head(curr_node)
self.table[index] = ll

curr_node = temp
self.count += 1

def check_and_resize(self):
if self.get_load_factor() > 0.7:
self.resize(self.capacity * 2)
elif self.get_load_factor() < 0.2:
self.resize(self.capacity / 2)



Expand All @@ -139,6 +234,12 @@ def resize(self, new_capacity):
for i in range(1, 13):
print(ht.get(f"line_{i}"))

ht.delete("line_1")

print("deleted")
for i in range(1, 13):
print(ht.get(f"line_{i}"))

# Test resizing
old_capacity = ht.get_num_slots()
ht.resize(ht.capacity * 2)
Expand Down
Loading