Design a Least Recently Used (LRU) cache with a fixed capacity:
LRUCache(capacity) — create the cache with the given positive capacity.get(key) — return the value for key if present, else -1. A get counts as a use.put(key, value) — insert or update key. If the cache exceeds its capacity, evict the least recently used key.Both get and put must run in O(1) average time. (The first operation names the class and its capacity argument.)
Input: capacity=2; put(1,1),put(2,2),get(1),put(3,3),get(2) Output: 1, -1 get(1) returns 1; put(3,3) evicts key 2, so get(2) returns -1.
Input: capacity=1; put(2,1),get(2),put(3,2),get(2) Output: 1, -1 put(3,2) evicts key 2 (capacity 1), so the later get(2) returns -1.
- 1 <= capacity <= 3000 - 0 <= key <= 10^4 - 0 <= value <= 10^5 - At most 2 * 10^5 calls to get and put
Two requirements pull in opposite directions: O(1) lookup by key (a hash map) and O(1) ordering by recency (so you can find and evict the least-recently-used item). Combine them: a hash map from key to node, and a doubly linked list ordering nodes from most- to least-recently used. Every access moves a node to the front; eviction removes the node at the back.
“Does get count as a use?”
Yes — a successful get makes that key the most recently used.
“When does eviction happen?”
On a put that would exceed capacity — evict the least recently used key first.
I need O(1) lookup and O(1) recency updates, so I pair a hash map with a doubly linked list.
The map points key to its list node; the list orders nodes most- to least-recently used.
Every get or put moves the node to the front; a put over capacity removes the node at the back.
Worked example — capacity = 2: put(1,1), put(2,2), get(1), put(3,3), get(2)
put(1,1) -> {1} (null)
put(2,2) -> {2, 1} (null)
get(1) -> 1; move 1 to front -> {1, 2}
put(3,3) -> over capacity, evict 2 -> {3, 1} (null)
get(2) -> -1 (2 was evicted)
The hash map gives O(1) lookup; the doubly linked list gives O(1) recency reordering and O(1) access to the LRU node at the tail.
get and a put on an existing key both mark it most-recently used by unlinking and re-inserting at the front.
When capacity is exceeded, the node just before the tail sentinel is the least-recently used; remove it from the list and the map.
Key takeaway
Hash map (key -> node) plus a doubly linked list (most- to least-recently used). get/put unlink the touched node and re-insert it at the front; a put over capacity evicts the node before the tail. Sentinels make every pointer operation O(1).
get(key): if absent return -1; move node to front; return value
put(k,v): if present update + move to front
else add node at front + map; if size > cap: remove tail.prev from list and map