Katkat

Implement an LRU Cache

ByteDanceSoftware Engineer · Technical Screen

Last updated

This problem tests your ability to combine a hash map and a doubly linked list to achieve constant-time performance for cache operations.

Problem Statement

Design and implement a data structure for a Least Recently Used (LRU) Cache. It should support the following operations: get and put.

Implement the LRUCache class:

  • LRUCache(int capacity): Initialize the LRU cache with positive size capacity.
  • int get(int key): Return the value of the key if the key exists, otherwise return -1.
  • void put(int key, int value): Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.

The get and put operations must each run in average O(1) time complexity.

Interface

class LRUCache:
    def __init__(self, capacity: int):
        pass

    def get(self, key: int) -> int:
        pass

    def put(self, key: int, value: int) -> None:
        pass

Example

Input:
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]

Output:
[null, null, null, 1, null, -1, null, -1, 3, 4]

Explanation:
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1);    // returns 1, cache is {2=2, 1=1}
lRUCache.put(3, 3); // evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2);    // returns -1 (not found)
lRUCache.put(4, 4); // evicts key 1, cache is {3=3, 4=4}
lRUCache.get(1);    // returns -1 (not found)
lRUCache.get(3);    // returns 3
lRUCache.get(4);    // returns 4

Constraints

  • 1 <= capacity <= 3000
  • 0 <= key <= 10,000
  • 0 <= value <= 10^5
  • At most 2 * 10^5 calls will be made to get and put.

Hints

Hint 1

To achieve O(1) access, you need a Hash Map to store keys and their corresponding nodes.

Hint 2

To achieve O(1) eviction and updates, use a Doubly Linked List to maintain the order of usage.

Hint 3

When a key is accessed or updated, move its corresponding node to the head of the linked list to mark it as most recently used.

Follow-up questions

  • How would you modify your implementation to be thread-safe in a multi-threaded environment?
  • What changes would be required if you needed to implement an LFU (Least Frequently Used) cache instead?
  • If the cache size were massive (e.g., millions of entries), how might you handle memory constraints or distributed caching?