Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
Implement the LRUCache class:
LRUCache(int capacity)Initialize the LRU cache with positive sizecapacity.int get(int key)Return the value of thekeyif the key exists, otherwise return-1.void put(int key, int value)Update the value of thekeyif thekeyexists. Otherwise, add thekey-valuepair to the cache. If the number of keys exceeds thecapacityfrom this operation, evict the least recently used key.
The functions get and put must each run in O(1) average time complexity.
solution
We use a linked list, more specifically, a doubly linked list to keep the nodes in the order of most recently used.
Then, to enable constant-time lookup of a particular node in the DLL by key, use a hashmap to map the key values to the nodes in the list.
class DLL:
def __init__(self):
# head <---> tail
# head is most recent, tail is least
self.head = DLLNode(None,None)
self.tail = DLLNode(None,None)
self.head.right = self.tail
self.tail.left = self.head
def insert_at_head(self, node):
nex = self.head.right
self.head.right = node
node.left = self.head
node.right = nex
nex.left = node
def remove_node(self, node):
prev, nex = node.left, node.right
prev.right = nex
nex.left = prev
def remove_at_tail(self):
key = self.tail.left.key
self.remove_node(self.tail.left)
return key
def __str__(self):
res = ""
cur = self.head.right
while cur != self.tail:
res += f"left: {cur.left.val} --- {cur.key}, {cur.val} --- right: {cur.right.val}"
res += "\n"
cur = cur.right
return res
class DLLNode:
def __init__(self, key, val):
self.key = key
self.val = val
self.left = None
self.right = None
class LRUCache:
def __init__(self, capacity: int):
self.DLL = DLL()
# key: Node
self.d = {}
self.capacity = capacity
self.size = 0
def _move_node_to_head(self, node):
self.DLL.remove_node(node)
self.DLL.insert_at_head(node)
def get(self, key: int) -> int:
if key not in self.d:
return -1
node = self.d[key]
self._move_node_to_head(node)
return node.val
def put(self, key: int, value: int) -> None:
if key in self.d:
self.d[key].val = value
self._move_node_to_head(node)
return
new_node = DLLNode(key, value)
self.d[key] = new_node
self.DLL.insert_at_head(new_node)
self.size += 1
if self.size > self.capacity:
popped_key = self.DLL.remove_at_tail()
del self.d[popped_key]
self.size -= 1