146. LRU Cache
Asked at Google, Meta, Amazon, Microsoft and 9 more
Difficulty
Medium
Time
O(1) per get and per put
Space
O(capacity)
Pattern
Hash Table
Companies
13
Problem
Design a data structure called an LRU cache (Least Recently Used cache). It stores at most capacity key-value pairs. When it is full and you add a new pair, it must throw away the pair that was used least recently.
Implement a class LRUCache with two methods. get(key) returns the stored value for that key, or -1 if the key is not present. put(key, value) inserts a new pair, or overwrites the value if the key already exists.
Using a key counts as using it. Both get(key) and put(key, value) must mark that key as the most recently used one. When the cache is over capacity after a put, remove the key that has gone the longest without being touched.
Both methods must run in O(1) average time. That rules out scanning a list to find the oldest key.
A full cache must drop the oldest key
Constraints
- ●
1 <= capacity <= 3000 - ●
0 <= key <= 10^4 - ●
0 <= value <= 10^5 - ●
At most 2 * 10^5 calls will be made to get and put
Examples
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]][null, null, null, 1, null, -1, null, -1, 3, 4]["LRUCache", "put", "get"]
[[1], [2, 2], [2]][null, null, 2]["LRUCache", "put", "put", "get"]
[[1], [2, 2], [3, 3], [2]][null, null, null, -1]Asked At
13 companies| Company | Difficulty | |
|---|---|---|
| Medium | View all Google questions → | |
| Meta | Medium | View all Meta questions → |
| Amazon | Medium | View all Amazon questions → |
| Microsoft | Medium | View all Microsoft questions → |
| Apple | Medium | View all Apple questions → |
| Netflix | Medium | View all Netflix questions → |
| Uber | Medium | View all Uber questions → |
| Atlassian | Medium | View all Atlassian questions → |
| Salesforce | Medium | View all Salesforce questions → |
| Bloomberg | Medium | View all Bloomberg questions → |
| Goldman Sachs | Medium | View all Goldman Sachs questions → |
| TikTok | Medium | View all TikTok questions → |
| NVIDIA | Medium | View all NVIDIA questions → |
How to Think About It
Two data structures needed: a hash map for O(1) key lookup, and a doubly linked list for O(1) insertion/deletion and maintaining access order.
The hash map stores key → node. The doubly linked list stores key-value pairs in access order: most recently used at the front, least recently used at the back.
On get or put: move the accessed node to the front of the list. This marks it as recently used.
On eviction (put when full): remove the node at the back of the list (least recently used). Also remove it from the hash map.
Dummy head and tail nodes simplify edge cases — you never need to check for null pointers during insertion/deletion.
Optimal Approach
Data structures:
Hash map: key → node in doubly linked list- Doubly linked list: key-value pairs in access order (most recent at front)
get(key):
- If key not in map, return -1
- Move the node to front (mark as recently used)
- Return the value
put(key, value):
- If key exists: update value, move to front
- If key doesn't exist:
a. Create new node, add to front, add to map
b. If over capacity: remove back node from list AND map
Both operations are O(1) because:
Hash maplookup:O(1)- Doubly linked list insertion/deletion (given the node):
O(1)
Hash map plus doubly linked list
What Trips People Up in Real Interviews
Not understanding the O(1) requirement for both get and put. A hash map alone gives O(1) get but O(n) eviction. A linked list alone gives O(1) eviction but O(n) lookup. You need both.
Confusing "least recently used" with "least frequently used." LRU evicts the item that was accessed longest ago, not the one accessed least often.
Forgetting to update access order on get. Every time you access an item (get or put), it becomes the most recently used. Move it to the tail of the linked list.
Not handling the case where the cache is full. When adding a new item and the cache is at capacity, evict the head of the linked list (least recently used) before adding.
Not storing the key in the linked list node. When evicting the LRU node, you need the key to remove it from the hash map. If the node only stores the value, you can't efficiently remove it.
Solution Code
O(1) per get and per put timeclass Node:
def __init__(self, key=0, val=0):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity):
self.cap = capacity
self.map = {}
self.head = Node()
self.tail = Node()
self.head.next = self.tail
self.tail.prev = self.head
def _remove(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def _add_front(self, node):
node.next = self.head.next
node.prev = self.head
self.head.next.prev = node
self.head.next = node
def get(self, key):
if key in self.map:
node = self.map[key]
self._remove(node)
self._add_front(node)
return node.val
return -1
def put(self, key, value):
if key in self.map:
self._remove(self.map[key])
node = Node(key, value)
self._add_front(node)
self.map[key] = node
if len(self.map) > self.cap:
lru = self.tail.prev
self._remove(lru)
del self.map[lru.key]Frequently Asked Questions
What is the LRU Cache problem?
Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. Implement get and put operations in `O(1)` time. This is a classic system design and coding hybrid question.
How do you solve LRU Cache?
The optimal approach runs in O(1) per get and per put time and O(capacity) space. Scroll up to "Optimal Approach" for the step-by-step walkthrough, diagrams, and code in Python, C++ and Java.
What companies ask LRU Cache?
LRU Cache is asked at Google, Meta, Amazon, Microsoft, Apple, Netflix, Uber, Atlassian, Salesforce, Bloomberg, Goldman Sachs, TikTok, NVIDIA. It is a medium difficulty problem.
What are common mistakes on LRU Cache?
- Not understanding the `O(1)` requirement for both get and put. A `hash map` alone gives `O(1)` get but `O(n)` eviction. A linked list alone gives `O(1)` eviction but `O(n)` lookup. You need both.
- Confusing "least recently used" with "least frequently used." LRU evicts the item that was accessed longest ago, not the one accessed least often.
- Forgetting to update access order on get. Every time you access an item (get or put), it becomes the most recently used. Move it to the tail of the linked list.
- Not handling the case where the cache is full. When adding a new item and the cache is at capacity, evict the head of the linked list (least recently used) before adding.
- Not storing the key in the linked list node. When evicting the LRU node, you need the key to remove it from the `hash map`. If the node only stores the value, you can't efficiently remove it.