Lesson 41 / 42

LRU Cache Design

Combine a hash map with a doubly linked list to get O(1) get/put with least-recently-used eviction.

Two structures, one job

An LRU cache needs O(1) get, O(1) put, and O(1) eviction of the least-recently-used item. A hash map alone gives fast lookup but no ordering; a linked list alone gives ordering but slow lookup. Combining them gives both.

Doubly linked list + hash map

The hash map stores key -> node for O(1) lookup. The doubly linked list keeps nodes in use-order: move a node to the front (most recent) on access, and evict from the back (least recent) when full.

class Node:
    def __init__(self, key, val):
        self.key, self.val = key, val
        self.prev = self.next = None

class LRUCache:
    def __init__(self, capacity):
        self.cap = capacity
        self.map = {}
        self.head = Node(0, 0)   # dummy most-recent end
        self.tail = Node(0, 0)   # dummy least-recent end
        self.head.next, self.tail.prev = self.tail, self.head

    def _remove(self, node):
        node.prev.next, node.next.prev = node.next, node.prev

    def _add_front(self, node):
        node.next, node.prev = self.head.next, self.head
        self.head.next.prev = node
        self.head.next = node

    def get(self, key):
        if key not in self.map:
            return -1
        node = self.map[key]
        self._remove(node)
        self._add_front(node)
        return node.val

    def put(self, key, val):
        if key in self.map:
            self._remove(self.map[key])
        node = Node(key, val)
        self.map[key] = node
        self._add_front(node)
        if len(self.map) > self.cap:
            lru = self.tail.prev
            self._remove(lru)
            del self.map[lru.key]

Python shortcut

In interviews you build it by hand, but in real Python code collections.OrderedDict already does this — move_to_end() on access and popitem(last=False) to evict the oldest.

Quick check

Test your understanding of the design.

Quick check: Why does LRU cache need a doubly linked list instead of a singly linked list?

  • It uses less memory
  • It allows O(1) removal of a node given only a pointer to it
  • It sorts keys alphabetically
  • Hash maps require doubly linked lists
Answer

It allows O(1) removal of a node given only a pointer to it — With a prev pointer, a node can unlink itself in O(1); a singly linked list would need O(n) to find the previous node.