LRU Cache
Company: Anthropic
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates a candidate's ability to design a data structure combining a hash map with a doubly linked list to achieve constant-time access and updates. It tests practical application of algorithmic design under strict complexity constraints, commonly used in coding interviews to assess handling of edge cases like eviction order and pointer maintenance.
Constraints
- 1 <= capacity <= 10^5
- 1 <= len(ops) <= 2 * 10^5
- Each op is ["GET", key] or ["PUT", key, value]; keys and values are integers represented as strings.
- Every GET and PUT must run in O(1) average time.
- Updating an existing key must not grow the size or evict; capacity = 1 must still work.
Examples
Input: (2, [["PUT", "1", "10"], ["PUT", "2", "20"], ["GET", "1"], ["PUT", "3", "30"], ["GET", "2"], ["GET", "3"]])
Expected Output: ["10", "-1", "30"]
Explanation: GET 1 marks key 1 most-recently used, so inserting key 3 (over capacity 2) evicts key 2. GET 2 misses; keys 1 and 3 remain.
Input: (1, [["PUT", "1", "1"], ["PUT", "2", "2"], ["GET", "1"], ["GET", "2"]])
Expected Output: ["-1", "2"]
Explanation: Capacity 1: PUT 2 evicts key 1, so GET 1 misses and GET 2 returns 2.
Input: (2, [["PUT", "1", "1"], ["PUT", "2", "2"], ["PUT", "1", "10"], ["PUT", "3", "3"], ["GET", "1"], ["GET", "2"], ["GET", "3"]])
Expected Output: ["10", "-1", "3"]
Explanation: Updating existing key 1 does not grow size but marks it MRU, so PUT 3 evicts key 2 (the LRU). Key 1 now holds the updated value 10.
Input: (2, [["PUT", "1", "1"], ["PUT", "2", "2"], ["GET", "1"], ["PUT", "3", "3"], ["GET", "2"]])
Expected Output: ["1", "-1"]
Explanation: GET 1 refreshes key 1's recency, so PUT 3 evicts key 2; GET 2 then misses.
Input: (1, [["GET", "5"]])
Expected Output: ["-1"]
Explanation: GET on an empty cache misses and returns -1.
Input: (2, [["PUT", "1", "100"], ["GET", "1"], ["PUT", "1", "200"], ["GET", "1"]])
Expected Output: ["100", "200"]
Explanation: Repeated updates of the same key return the latest stored value without eviction.
Hints
- Combine a hash map (key -> node) for O(1) lookup with a doubly linked list that orders nodes by recency.
- Use sentinel head/tail nodes so insert-at-front and remove never hit a null-pointer edge case, even at capacity 1.
- On both GET (hit) and PUT (existing key), unlink the node and re-insert it at the front to mark it most-recently used; only insert a brand-new key triggers an eviction of tail.prev.