From 77acaaf62925f7963e33e55b1de9ae1a432a9230 Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 28 Oct 2020 20:42:42 -0500 Subject: [PATCH 1/4] initial commit --- hashtable/hashtable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index 0205f0ba9..b9f77e019 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -30,7 +30,7 @@ def get_num_slots(self): table data. (Not the number of items stored in the hash table, but the number of slots in the main list.) - One of the tests relies on this. + One of the tests relies on this. if froggy do tonight 🤣 Implement this. """ From 0bd1c38b1cfeb6e869c981edbafcbf5f262487fd Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 28 Oct 2020 21:20:47 -0500 Subject: [PATCH 2/4] Finished day 1 --- README.md | 2 +- hashtable/hashtable.py | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f766a6189..61c218700 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Hash Tables - +### https://github.com/LambdaSchool/cs-module-project-hash-tables/pull/173 ## Day 1 Task: Implement a basic hash table without collision resolution. diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index b9f77e019..7976f76ed 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -21,8 +21,9 @@ class HashTable: """ def __init__(self, capacity): - # Your code here - + self.capacity = MIN_CAPACITY + self.table = [None] * self.capacity + self.count = 0 def get_num_slots(self): """ @@ -35,6 +36,7 @@ def get_num_slots(self): Implement this. """ # Your code here + return len(self.table) def get_load_factor(self): @@ -63,7 +65,10 @@ def djb2(self, key): 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): """ @@ -82,7 +87,21 @@ def put(self, key, value): Implement this. """ # Your code here - + index = self.hash_index(key) + #storing linked list with + node = HashTableEntry(key, value) + #print(node) + key = self.table[index] + self.count += 1 + + #does it exist + if key: + self.table[index] = node + self.table[index].next = key + else: + self.table[index] = node + print(self.table) + return self.table def delete(self, key): """ @@ -93,6 +112,8 @@ def delete(self, key): Implement this. """ # Your code here + self.count -= 1 + self.put(key, None) def get(self, key): @@ -104,6 +125,13 @@ def get(self, key): Implement this. """ # Your code here + item = self.hash_index(key) + storage = self.table[item] + + while storage: + if storage.key == key: + return storage.value + storage = storage.next def resize(self, new_capacity): From b5b823419186ed3e52ed7099a01d34ccc01ed5b5 Mon Sep 17 00:00:00 2001 From: Anthony Date: Sun, 1 Nov 2020 23:31:57 -0600 Subject: [PATCH 3/4] hashtable complete --- hashtable/hashtable.py | 155 +++++++++++++++++++++++++++++------------ 1 file changed, 109 insertions(+), 46 deletions(-) diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index 7976f76ed..f2dcb57d4 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -8,6 +8,58 @@ def __init__(self, key, value): self.next = None +class LinkedList: + def __init__(self): + self.head = None + + def __repr__(self): + current_str = "" + current = self.head + while current is not None: + current_str += f"{str(current.value)} -> " + current = current.next + return current_str + + def add_to_head(self, node): + node.next = self.head + self.head = node + + def add_to_head_or_overwrite(self, node): + existing_node = self.find(node.key) + if existing_node is not None: + existing_node.key = node.key + else: + self.add_to_head(node) + + def find(self, key): + current = self.head + while current is not None: + if current.key == key: + return current.value + current = current.next + return None + + def delete(self, key): + current = self.head + + if current.key == key: + self.head = current.next + + prev = current + current = current.next + + while current is not None: + if current.key == key: + prev.next = current.next + current.next = None + return current.value + else: + prev = current + current = current.next + + return None + + # Hash table can't have fewer than this many slots MIN_CAPACITY = 8 @@ -16,23 +68,27 @@ class HashTable: """ A hash table that with `capacity` buckets that accepts string keys - Implement this. """ def __init__(self, capacity): - self.capacity = MIN_CAPACITY - self.table = [None] * self.capacity - self.count = 0 + # Your code here + self.capacity = capacity + self.table = [None] * capacity + + for i in range(0, len(self.table)): + self.table[i] = LinkedList() + def get_num_slots(self): """ Return the length of the list you're using to hold the hash table data. (Not the number of items stored in the hash table, but the number of slots in the main list.) - - One of the tests relies on this. if froggy do tonight 🤣 - + One of the tests relies on this. + + One of the tests relies on this. if froggy do tonight 🤣 + Implement this. """ # Your code here @@ -42,33 +98,51 @@ def get_num_slots(self): def get_load_factor(self): """ Return the load factor for this hash table. - Implement this. """ # Your code here + count = 0 + + for i in range(0, len(self.table)): + current = self.table[i].head + while current is not None: + count += 1 + current = current.next + + load_factor = count / self.get_num_slots() + + if load_factor > 0.7: + self.resize(self.capacity * 2) + elif load_factor < 0.2: + if self.capacity // 2 < 8: + self.resize(8) + else: + self.resize(self.capacity // 2) + + return load_factor 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 x in key: - hash = ((hash << 5) + hash) + ord(x) - return hash & 0xffffffff + for c in key: + hash = (hash * 33) + ord(c) + return hash + def hash_index(self, key): """ @@ -78,71 +152,60 @@ def hash_index(self, key): #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 - index = self.hash_index(key) - #storing linked list with - node = HashTableEntry(key, value) - #print(node) - key = self.table[index] - self.count += 1 - - #does it exist - if key: - self.table[index] = node - self.table[index].next = key - else: - self.table[index] = node - print(self.table) - return self.table + new_entry = HashTableEntry(key, value) + self.table[self.hash_index(key)].add_to_head(new_entry) + 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 - self.count -= 1 - self.put(key, None) + return self.table[self.hash_index(key)].delete(key) def get(self, key): """ Retrieve the value stored with the given key. - Returns None if the key is not found. - Implement this. """ # Your code here - item = self.hash_index(key) - storage = self.table[item] - - while storage: - if storage.key == key: - return storage.value - storage = storage.next + if self.table[self.hash_index(key)].head is not None: + return self.table[self.hash_index(key)].find(key) + else: + return None def resize(self, new_capacity): """ Changes the capacity of the hash table and rehashes all key/value pairs. - Implement this. """ # Your code here + old_capacity = self.capacity + self.capacity = new_capacity + for i in range(old_capacity, new_capacity): + self.table.append(None) + self.table[i] = LinkedList() + + for i in range(0, old_capacity): + current_node = self.table[i].head + while current_node is not None: + self.put(current_node.key, current_node.value) + current_node = current_node.next if __name__ == "__main__": @@ -174,8 +237,8 @@ def resize(self, new_capacity): print(f"\nResized from {old_capacity} to {new_capacity}.\n") - # Test if data intact after resizing + # # Test if data intact after resizing for i in range(1, 13): print(ht.get(f"line_{i}")) - print("") + print("") \ No newline at end of file From e6c382170a0b301ed1eb0cbe1b7723fd4dee7807 Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 4 Nov 2020 15:32:51 -0600 Subject: [PATCH 4/4] lookup table, expensive seq, word count, and no dupl finished --- README.md | 8 ++++---- applications/expensive_seq/expensive_seq.py | 13 +++++++++++- applications/lookup_table/lookup_table.py | 19 +++++++++++++++--- applications/no_dups/no_dups.py | 9 +++++++++ applications/word_count/word_count.py | 22 +++++++++++++++++---- 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 61c218700..27d824447 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,10 @@ For these, you can use either the built-in `dict` type, or the hashtable you built. (Some of these are easier with `dict` since it's more full-featured.) -* [Lookup Table](applications/lookup_table/) -* [Expensive Sequence](applications/expensive_seq/) -* [Word Count](applications/word_count/) -* [No Duplicates](applications/no_dups/) +* XXXXX[Lookup Table](applications/lookup_table/) +* XXXXX[Expensive Sequence](applications/expensive_seq/) +* XXXXX[Word Count](applications/word_count/) +* XXXXX[No Duplicates](applications/no_dups/) * [Markov Chains](applications/markov/) * [Histogram](applications/histo/) * [Cracking Caesar Ciphers](applications/crack_caesar/) diff --git a/applications/expensive_seq/expensive_seq.py b/applications/expensive_seq/expensive_seq.py index 5c82b8453..1ebad124d 100644 --- a/applications/expensive_seq/expensive_seq.py +++ b/applications/expensive_seq/expensive_seq.py @@ -1,8 +1,19 @@ # Your code here +table = {} + def expensive_seq(x, y, z): - # Your code here + if x <= 0: + return y + z + + if (x, y, z) not in table: + table[(x, y, z)] = ( + expensive_seq(x - 1, y + 1, z) + + expensive_seq(x - 2, y + 2, z * 2) + + expensive_seq(x - 3, y + 3, z * 3) + ) + return table[(x, y, z)] diff --git a/applications/lookup_table/lookup_table.py b/applications/lookup_table/lookup_table.py index 05b7d37fa..fa13c5b47 100644 --- a/applications/lookup_table/lookup_table.py +++ b/applications/lookup_table/lookup_table.py @@ -1,5 +1,6 @@ # Your code here - +import math +import random def slowfun_too_slow(x, y): v = math.pow(x, y) @@ -9,13 +10,25 @@ def slowfun_too_slow(x, y): return v +def build_lookup_table(): + d = {} + + for x in range(2, 14): + for y in range(3, 6): + d[(x, y)] = slowfun_too_slow(x, y) + + return d + +# this ensures that the lookup table is ALREADY BUILT before we call slowfun() in our for loop! +table = build_lookup_table() + 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 - + return table[(x, y)] # Do not modify below this line! @@ -23,4 +36,4 @@ def slowfun(x, y): for i in range(50000): x = random.randrange(2, 14) y = random.randrange(3, 6) - print(f'{i}: {x},{y}: {slowfun(x, y)}') + print(f'{i}: {x},{y}: {slowfun(x, y)}') \ No newline at end of file diff --git a/applications/no_dups/no_dups.py b/applications/no_dups/no_dups.py index caa162c8c..ff50c92be 100644 --- a/applications/no_dups/no_dups.py +++ b/applications/no_dups/no_dups.py @@ -1,6 +1,15 @@ def no_dups(s): # Your code here + d = {} + arr = s.split() + output = [] + for word in arr: + if word not in d: + d[word] = 1 + output.append(word) + + return " ".join(output) if __name__ == "__main__": diff --git a/applications/word_count/word_count.py b/applications/word_count/word_count.py index a20546425..277db5961 100644 --- a/applications/word_count/word_count.py +++ b/applications/word_count/word_count.py @@ -1,10 +1,24 @@ -def word_count(s): - # Your code here - +def word_count(string): + separators = '":;,.-+=/\|[]{}()*^&' + + words = string.lower().split() + counts = {} + + for word in words: + word = word.strip(separators) + if not word: + break + if word in counts: + counts[word] += 1 + else: + counts[word] = 1 + + return counts +# Driver program if __name__ == "__main__": print(word_count("")) print(word_count("Hello")) print(word_count('Hello, my cat. And my cat doesn\'t say "hello" back.')) - print(word_count('This is a test of the emergency broadcast network. This is only a test.')) \ No newline at end of file + print(word_count('This is a test of the emergency broadcast network. This is only a test.'))