Katkat

Process Get and Set Operations in a Key-Value Cache

BalyasnySoftware Engineer · Onsite

Last updated

This problem tests your ability to implement an efficient, thread-safe, and memory-constrained LRU (Least Recently Used) cache using a combination of a hash map and a doubly linked list.

LRU Cache Implementation

Design and implement a data structure for an LRU (Least Recently Used) cache. The cache should support get(key) and put(key, value) operations with the following requirements:

  • get(key): Returns the value of the key if it exists, otherwise returns -1.
  • put(key, value): Updates the value of the key if it exists. Otherwise, adds the key-value pair to the cache. If the cache reaches its capacity, it should invalidate the least recently used item before inserting a new item.

Implement the class LRUCache:

class LRUCache:
    def __init__(self, capacity: int):
        # Initialize the cache with a positive size capacity
        pass

    def get(self, key: int) -> int:
        # Return the value of the key if the key exists, otherwise -1
        pass

    def put(self, key: int, value: int) -> None:
        # Update the value of the key if it exists, otherwise add the pair
        pass

Constraints

  • 1 <= capacity <= 100,000
  • 0 <= key <= 1,000,000
  • 0 <= value <= 1,000,000,000
  • Operations should run in O(1) average time complexity.

Examples

lru = LRUCache(2)
lru.put(1, 1)    # Cache is {1=1}
lru.put(2, 2)    # Cache is {1=1, 2=2}
lru.get(1)       # Returns 1, Cache is {2=2, 1=1}
lru.put(3, 3)    # Evicts key 2, Cache is {1=1, 3=3}
lru.get(2)       # Returns -1 (not found)

Hints

Hint 1

Use a hash map to store keys mapped to nodes in a doubly linked list to achieve O(1) access and removal.

Hint 2

The doubly linked list should maintain the order of use: move the accessed or newly added node to the head, and remove from the tail when capacity is exceeded.

Hint 3

Ensure you update both the hash map and the linked list pointers consistently whenever a 'get' or 'put' operation occurs.

Follow-up questions

  • How would you modify this implementation to be thread-safe in a multi-threaded environment?
  • If keys are accessed with different frequencies, would an LRU cache still be the most effective strategy, or would an LFU (Least Frequently Used) cache be better?
  • How would you handle cache persistence if the system needs to recover from a process crash?