diff --git a/doubly_linked_list/dll.py b/doubly_linked_list/dll.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/doubly_linked_list/doubly_linked_list.py b/doubly_linked_list/doubly_linked_list.py index be385fb098..38d7cc6075 100644 --- a/doubly_linked_list/doubly_linked_list.py +++ b/doubly_linked_list/doubly_linked_list.py @@ -52,12 +52,19 @@ def __len__(self): as the new head of the list. Don't forget to handle the old head node's previous pointer accordingly.""" def add_to_head(self, value): + node = ListNode(value, None, self.head) + self.head.prev = node + self.head = node pass """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): + nextNode = self.head.next + nextNode.prev = nextNode + self.head = nextNode + self.delete() pass """Wraps the given value in a ListNode and inserts it diff --git a/lru_cache/check.py b/lru_cache/check.py new file mode 100644 index 0000000000..9c5e305a6d --- /dev/null +++ b/lru_cache/check.py @@ -0,0 +1,82 @@ +from doubly_linked_list import DoublyLinkedList + +class LRUCache: + """ + Our LRUCache class keeps track of the max number of nodes it + can hold, the current number of nodes it is holding, a doubly- + linked list that holds the key-value entries in the correct + order, as well as a storage dict that provides fast access + to every node stored in the cache. + """ + def __init__(self, limit=10): + self.limit = limit + self.current_no_of_nodes = 0 + self.cache = {} + self.storage = DoublyLinkedList() + + """ + Retrieves the value associated with the given key. Also + needs to move the key-value pair to the end of the order + such that the pair is considered most-recently used. + Returns the value associated with the key or None if the + key-value pair doesn't exist in the cache. + """ + def get(self, key): + if key in self.cache: + value = self.cache[key] + node = self.storage.head + while node: + eachKey = list(node.value.keys())[0] + if eachKey == key: + self.storage.move_to_end(node) + break + return value + + + """ + Adds the given key-value pair to the cache. The newly- + added pair should be considered the most-recently used + entry in the cache. If the cache is already at max capacity + before this entry is added, then the oldest entry in the + cache needs to be removed to make room. Additionally, in the + case that the key already exists in the cache, we simply + want to overwrite the old value associated with the key with + the newly-specified value. + """ + def set(self, key, value): + if key in self.cache: + node = self.storage.head + while node: + # eack key from the LRU cache DLL + eachKey = (node.value.keys())[0] + if eachKey == key: + # Update to LRU cache + node.value[key] = value + # Add to memoization cache + self.cache[key] = value + break + node = node.next + + # remove from head to make room + if self.current_no_of_nodes == self.limit: + self.storage.remove_from_head() + # add to LRU + self.storage.add_to_tail({ key: value }) + # add to memoization cache + self.cache[key] = value + self.current_no_of_nodes += 1 + + def __str__(self): + return f'storage: {self.storage.head.value} cache: {self.cache}' + + + +# cache = LRUCache(3) +# cache.set('item1', 'a') +# cache.set('item1', 'b') +# cache.set('item3', 'c') +# print(cache.__str__()) + + + + # self.storage.add_to_tail({key: value}) diff --git a/lru_cache/doubly_linked_list.py b/lru_cache/doubly_linked_list.py index a2bee75ec3..5a1546d0b6 100644 --- a/lru_cache/doubly_linked_list.py +++ b/lru_cache/doubly_linked_list.py @@ -1,38 +1,40 @@ """Each ListNode holds a reference to its previous node as well as its next node in the List.""" - class ListNode: - def __init__(self, value, prev=None, next=None): - self.value = value - self.prev = prev - self.next = next - - """Wrap the given value in a ListNode and insert it - after this node. Note that this node could already - have a next node it is point to.""" - def insert_after(self, value): - current_next = self.next - self.next = ListNode(value, self, current_next) - if current_next: - current_next.prev = self.next - - """Wrap the given value in a ListNode and insert it - before this node. Note that this node could already - have a previous node it is point to.""" - def insert_before(self, value): - current_prev = self.prev - self.prev = ListNode(value, current_prev, self) - if current_prev: - current_prev.next = self.prev - - """Rearranges this ListNode's previous and next pointers - accordingly, effectively deleting this ListNode.""" - def delete(self): - if self.prev: - self.prev.next = self.next - if self.next: - self.next.prev = self.prev + def __init__(self, value, prev=None, next=None): + self.value = value + self.prev = prev + self.next = next + + def __str__(self): + return f"Node: {self.value}, prev: {self.prev}, next: {self.next}" + + """Wrap the given value in a ListNode and insert it + after this node. Note that this node could already + have a next node it is point to.""" + def insert_after(self, value): + current_next = self.next + self.next = ListNode(value, self, current_next) + if current_next: + current_next.prev = self.next + + """Wrap the given value in a ListNode and insert it + before this node. Note that this node could already + have a previous node it is point to.""" + def insert_before(self, value): + current_prev = self.prev + self.prev = ListNode(value, current_prev, self) + if current_prev: + current_prev.next = self.prev + + """Rearranges this ListNode's previous and next pointers + accordingly, effectively deleting this ListNode.""" + 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 @@ -40,34 +42,117 @@ def delete(self): class DoublyLinkedList: - def __init__(self, node=None): - self.head = node - self.tail = node - self.length = 1 if node is not None else 0 - - def __len__(self): - return self.length - - def add_to_head(self, value): - pass - - def remove_from_head(self): - pass - - def add_to_tail(self, value): - pass - - def remove_from_tail(self): - pass - - def move_to_front(self, node): - pass - - def move_to_end(self, node): - pass - - def delete(self, node): - pass - - def get_max(self): - pass + def __init__(self, node=None): + self.head = node + self.tail = node + self.length = 1 if node is not None else 0 + + def __str__(self): + return f"head: {self.head}, tail: {self.tail}, length: {self.length}" + + def __len__(self): + return self.length + + """Wraps the given value in a ListNode and inserts it + as the new head of the list. Don't forget to handle + the old head node's previous pointer accordingly.""" + def add_to_head(self, value): + newNode = ListNode(value, None, None) + self.length += 1 + if not self.head and not self.tail: + self.head = newNode + self.tail = newNode + else: + newNode.next = self.head + self.head.prev = newNode + self.head = newNode + return newNode.value + + """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): + 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): + newNode = ListNode(value, None, None) + self.length += 1 + if not self.head and not self.tail: + self.head = newNode + self.tail = newNode + else: + newNode.prev = self.tail + self.tail.next = newNode + self.tail = newNode + return newNode.value + + """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): + value = self.tail.value + self.delete(self.tail) + 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): + if self.head is node: + return self.head.value + value = node.value + if self.tail is node: + self.remove_from_tail() + else: + node.delete() + self.length -= 1 + return self.add_to_head(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): + if self.tail is node: + return + value = node.value + if node is self.head: + self.remove_from_head() + else: + node.delete() + self.length -= 1 + + self.add_to_tail(value) + return value + + """Removes a node from the list and handles cases where + the node was the head or the tail""" + def delete(self, node): + if not self.head and not self.tail: + return False + self.length -= 1 + if self.head is self.tail: + self.head = None + self.tail = None + elif self.head is node: + self.head = node.next + node.delete() + elif self.tail is node: + self.tail = node.prev + node.delete() + else: + node.delete() + + """Returns the highest value currently in the list""" + def get_max(self): + if not self.head: + return None + node = self.head + maxVal = node.value + while node: + if node.value > maxVal: + maxVal = node.value + node = node.next + return maxVal \ No newline at end of file diff --git a/lru_cache/lru_cache.py b/lru_cache/lru_cache.py index 3a4b43a65d..9a343ddf07 100644 --- a/lru_cache/lru_cache.py +++ b/lru_cache/lru_cache.py @@ -1,3 +1,8 @@ +import sys +sys.path.append('./doubly_linked_list') +from doubly_linked_list import DoublyLinkedList + + class LRUCache: """ Our LRUCache class keeps track of the max number of nodes it @@ -7,7 +12,10 @@ class LRUCache: to every node stored in the cache. """ def __init__(self, limit=10): - pass + self.limit = limit + self.current_no_of_nodes = 0 + self.cache = {} + self.storage = DoublyLinkedList() """ Retrieves the value associated with the given key. Also @@ -17,7 +25,13 @@ def __init__(self, limit=10): key-value pair doesn't exist in the cache. """ def get(self, key): - pass + node = self.storage.head + while node: + if key == node.value.get(key, None): + self.storage.move_to_end(node) + break + node = node.next + return self.cache.get(key, None) """ Adds the given key-value pair to the cache. The newly- @@ -30,4 +44,32 @@ def get(self, key): the newly-specified value. """ def set(self, key, value): - pass + if self.current_no_of_nodes == self.limit: + self.storage.remove_from_head() + self.current_no_of_nodes -= 1 + + if key in self.cache: + node = self.storage.head + + while node: + if key == node.value.get(key, None): + node.value[key] = value + break + node = node.next + + self.storage.add_to_tail({key: value}) + self.current_no_of_nodes += 1 + self.cache[key] = value + + def __str__(self): + return f'storage: {self.storage.head.value} cache: {self.cache}' + +cache = LRUCache(3) +cache.set('item1', 'a') +# cache.set('item2', 'b') +# cache.set('item3', 'c') +# cache.set('item4', 'd') +# cache.set('item4', 'D') +# cache.set('item5', 'e') +# print(cache.__str__()) +print(cache.get('nonexistent')) diff --git a/queue_and_stack/dll_queue.py b/queue_and_stack/dll_queue.py index 039829ec2c..1f63b1c440 100644 --- a/queue_and_stack/dll_queue.py +++ b/queue_and_stack/dll_queue.py @@ -8,12 +8,15 @@ def __init__(self): self.size = 0 # Why is our DLL a good choice to store our elements? # self.storage = ? + self.storage = DoublyLinkedList() def enqueue(self, value): - pass + self.storage.add_to_tail(value) + self.size += 1 def dequeue(self): - pass + self.storage.remove_from_head() + self.size -= 1 def len(self): - pass + return self.size diff --git a/queue_and_stack/dll_stack.py b/queue_and_stack/dll_stack.py index a06d076176..913f9e566e 100644 --- a/queue_and_stack/dll_stack.py +++ b/queue_and_stack/dll_stack.py @@ -7,12 +7,14 @@ def __init__(self): self.size = 0 # Why is our DLL a good choice to store our elements? # self.storage = ? + self.storage = DoublyLinkedList() def push(self, value): - pass + self.storage.add_to_head(value) + self.size += 1 def pop(self): - pass - + self.storage.remove_from_head() + self.size -= 1 def len(self): - pass + return self.size