03
LRU in one paragraph
Every access moves the entry to the head of a doubly-linked list. When space is needed, drop the tail. O(1) insert, access, and evict. The structure: hash map (key → list node) + doubly-linked list. Every major language has a standard implementation. Redis's maxmemory-policy allkeys-lru is exactly this.
LRU's Achilles' heel: one-time sequential scans. A nightly job that reads 10M rows in order will kick every useful entry out of the cache. Every morning, you wake up to a cold cache and a degraded first hour.
LRU cache — hashmap + doubly-linked list
class Node:
__slots__ = "k", "v", "prev", "next"
def __init__(self, k, v):
self.k, self.v = k, v
self.prev = self.next = None
class LRU:
def __init__(self, cap):
self.cap, self.map = cap, {}
self.head, self.tail = Node(0, 0), Node(0, 0) # sentinels
self.head.next, self.tail.prev = self.tail, self.head
def _remove(self, n):
n.prev.next, n.next.prev = n.next, n.prev
def _add(self, n): # insert right after head
n.next = self.head.next; n.prev = self.head
self.head.next.prev = n; self.head.next = n
def get(self, k):
if k not in self.map: return -1
n = self.map[k]; self._remove(n); self._add(n)
return n.v
def put(self, k, v):
if k in self.map: self._remove(self.map[k])
elif len(self.map) == self.cap:
lru = self.tail.prev; self._remove(lru); del self.map[lru.k]
n = Node(k, v); self._add(n); self.map[k] = n