Implement an LRU Cache
Company: Shopify
Role: Machine Learning Engineer
Category: Coding & Algorithms
Difficulty: easy
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's competency in designing efficient cache mechanisms and managing time and space complexity for operations required to support least-recently-used eviction.
Constraints
- 1 <= capacity
- 0 <= number of operations <= 10^5
- Each operation is ["put", key, value] or ["get", key]
- Keys and values fit in a 32-bit signed integer (may be negative or zero)
- get and put must each run in O(1) average time
Examples
Input: (2, [["put", 1, 1], ["put", 2, 2], ["get", 1], ["put", 3, 3], ["get", 2], ["put", 4, 4], ["get", 1], ["get", 3], ["get", 4]])
Expected Output: [None, None, 1, None, -1, None, -1, 3, 4]
Explanation: The canonical example. get(1) makes key 1 most recently used, so put(3,3) evicts key 2 (get(2) -> -1). put(4,4) then evicts key 1 (get(1) -> -1). Keys 3 and 4 remain.
Input: (1, [["put", 1, 10], ["get", 1], ["put", 2, 20], ["get", 1], ["get", 2]])
Expected Output: [None, 10, None, -1, 20]
Explanation: Capacity 1: putting key 2 evicts key 1, so get(1) returns -1 while get(2) returns 20.
Input: (2, [["get", 1]])
Expected Output: [-1]
Explanation: get on an empty cache returns -1.
Input: (2, [["put", 1, 1], ["put", 1, 5], ["get", 1]])
Expected Output: [None, None, 5]
Explanation: Updating an existing key overwrites its value (1 -> 5) and refreshes recency.
Input: (3, [["put", 1, 1], ["put", 2, 2], ["put", 3, 3], ["get", 1], ["put", 4, 4], ["get", 2], ["get", 3], ["get", 4], ["get", 1]])
Expected Output: [None, None, None, 1, None, -1, 3, 4, 1]
Explanation: After filling 1,2,3, get(1) bumps key 1 to most-recent, so put(4,4) evicts the least-recently-used key 2; key 2 then misses while 3, 4, and 1 hit.
Input: (2, [["put", -1, -100], ["get", -1], ["put", 0, 0], ["put", 5, 5], ["get", -1]])
Expected Output: [None, -100, None, None, -1]
Explanation: Negative and zero keys/values work; after putting 0 and 5 into a size-2 cache, key -1 (least recently used) is evicted and returns -1.
Hints
- Combine a hash map (key -> node) with a doubly linked list ordering nodes from least- to most-recently used; this gives O(1) lookup plus O(1) move/evict.
- On get, if the key exists, move its node to the most-recently-used end before returning the value.
- On put for an existing key, update the value and move it to the most-recently-used end; for a new key, insert at that end and, if size now exceeds capacity, remove the node at the least-recently-used end.
- Python's collections.OrderedDict (move_to_end / popitem(last=False)) gives all of this for free, as does Java LinkedHashMap with access order.