Katkat

Implement an LRU cache

TikTokSoftware Engineer · Technical Screen

Last updated

This question tests the ability to implement a Least Recently Used (LRU) cache using a combination of a hash map and a doubly linked list to achieve constant time complexity for core operations.

LRU Cache Implementation

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

  • LRUCache(int capacity): Initialize the LRU cache with a 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 O(1) average time complexity.

Function Signature

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

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

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

Examples

Example 1:

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:

  1. Initialize cache with capacity 2.
  2. Put(1, 1), Put(2, 2) -> Cache is {1:1, 2:2}
  3. Get(1) -> Returns 1, cache order becomes {2:2, 1:1}
  4. Put(3, 3) -> Cache full, evict key 2. Cache is {1:1, 3:3}
  5. Get(2) -> Returns -1 (not found)
  6. Put(4, 4) -> Cache full, evict key 1. Cache is {3:3, 4:4}

Constraints

  • 1 <= capacity <= 3000
  • 0 <= key <= 10^4
  • 0 <= value <= 10^5
  • Total operations will not exceed 2 * 10^5.

Hints

Hint 1

To achieve O(1) time, consider using a hash map for fast lookups and a doubly linked list to maintain the order of usage.

Hint 2

The doubly linked list allows for O(1) removal and insertion of nodes if you maintain pointers to the head and tail.

Hint 3

Whenever a key is accessed via 'get' or 'put', move its corresponding node to the front (most recently used position) of the linked list.

Follow-up questions

  • How would you modify your implementation to be thread-safe in a multi-threaded environment?
  • What are the trade-offs of using a doubly linked list versus a single linked list for this problem?
  • If the cache needed to be distributed across multiple servers, how would you handle the eviction policy?