PracticeNeetCode ↗LeetCode ↗
Design a fixed-capacity Least Recently Used (LRU) cache supporting get(key) and put(key, value), both in O(1) average time. Both operations count as "using" that key; inserting past capacity evicts the least recently used entry.
key → node plus a doubly linked list with left (LRU-side) and right (MRU-side) dummy sentinels.get(key): if present, unlink the node and re-insert it at the MRU end, then return its value; otherwise return -1.put(key, value): if the key exists, update its value and move it to the MRU end; otherwise create a new node, insert it at the MRU end, and add it to the map.left sentinel (the LRU node) from both the list and the map.Time: O(1) for both get and put — Space: O(capacity)
class Node:
def __init__(self, key: int, val: int):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.cap = capacity
self.cache = {}
self.left = Node(0, 0)
self.right = Node(0, 0)
self.left.next = self.right
self.right.prev = self.left
def _remove(self, node: Node) -> None:
node.prev.next = node.next
node.next.prev = node.prev
def _insert(self, node: Node) -> None:
prev, nxt = self.right.prev, self.right
prev.next = node
node.prev = prev
node.next = nxt
nxt.prev = node
def get(self, key: int) -> int:
if key not in self.cache:
return -1
node = self.cache[key]
self._remove(node)
self._insert(node)
return node.val
def put(self, key: int, value: int) -> None:
if key in self.cache:
self._remove(self.cache[key])
node = Node(key, value)
self.cache[key] = node
self._insert(node)
if len(self.cache) > self.cap:
lru = self.left.next
self._remove(lru)
del self.cache[lru.key]How the solution above works, piece by piece.
cache maps key → node for O(1) lookup; the doubly linked list keeps usage order. left and right are sentinel nodes, so inserting and removing never need edge cases._remove unlinks a node by connecting its neighbours to each other; _insert puts a node just before right (the most-recent end).get: if present, move the node to the most-recent end and return its value; otherwise −1.put: remove any old node for the key, insert a fresh one at the most-recent end, and if the size is over capacity remove left.next (the least recently used) from both the list and the map.Running the example with capacity = 2: