Implement an O(1) LRU Cache
Company: Microsoft
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Implement `run_lru(capacity, operations)` to model a least-recently-used cache with integer keys and values.
Each operation is a three-integer row:
- `[0, key, value]` performs `put(key, value)` and produces no output.
- `[1, key, 0]` performs `get(key)`; the third value is padding. Append the stored value to the result, or append `-1` if the key is absent.
Both a successful `get` and a `put` of an existing key make that key most recently used. Inserting a new key at full capacity evicts the least recently used key. Capacity is positive.
Return the results of all `get` operations in order. Each operation should run in `O(1)` time.
```hint Combine lookup with ordering
A hash map locates a key's node, while a doubly linked list records recency from most recent to least recent.
```
```hint Isolate pointer updates
Use sentinel head and tail nodes plus helpers that remove a node and add a node at the most-recent end.
```
### Discussion Extensions
- Why does one lock around the entire cache limit concurrency, and why does segmented locking provide only approximate global LRU?
- How would lazy expiry plus background cleanup add per-entry expiration?
- What responsibilities change when keys are sharded across cache nodes?
Quick Answer: Implement an LRU cache whose get and put operations both run in O(1) time. Combine a hash map with a doubly linked list, then reason about eviction, recency updates, and extensions such as expiry or sharding.
Implement run_lru(capacity, operations). A row [0, key, value] performs put and a row [1, key, 0] performs get; return all get results in order, using -1 for a missing key. Successful gets and updates make a key most recently used, and insertion at capacity evicts the least recently used key.
Constraints
- 1 <= capacity <= 20.
- 0 <= operations.length <= 40, and every operation contains exactly three integers.
- Operation kinds are 0 for put and 1 for get; keys and values are between -1,000,000,000 and 1,000,000,000.
Examples
Input: (2, [[0, 1, 10], [0, 2, 20], [1, 1, 0], [0, 3, 30], [1, 2, 0], [1, 3, 0], [1, 1, 0]])
Expected Output: [10, -1, 30, 10]
Input: (2, [[0, 1, 1], [0, 2, 2], [0, 1, 10], [0, 3, 3], [1, 2, 0], [1, 1, 0], [1, 3, 0]])
Expected Output: [-1, 10, 3]
Hints
- Use a hash map for lookup and an ordering structure that can move or remove a known node in constant time.
- Treat updating an existing key as both a value change and a recency change.