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
Empty file added doubly_linked_list/dll.py
Empty file.
7 changes: 7 additions & 0 deletions doubly_linked_list/doubly_linked_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
82 changes: 82 additions & 0 deletions lru_cache/check.py
Original file line number Diff line number Diff line change
@@ -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})
209 changes: 147 additions & 62 deletions lru_cache/doubly_linked_list.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,158 @@
"""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
the list's head and tail nodes."""


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
48 changes: 45 additions & 3 deletions lru_cache/lru_cache.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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-
Expand All @@ -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'))
Loading