Katkat

Implement LRU/LFU cache with custom eviction

CitadelSoftware Engineer · Technical Screen

Last updated

This problem tests your ability to design a generic cache structure that supports interchangeable eviction strategies, requiring efficient lookup and metadata management.

Problem Statement

Design a thread-safe CustomCache class that supports a fixed capacity and allows for pluggable eviction policies. You must implement the data structure to handle put(key, value) and get(key) operations in O(1) average time complexity, regardless of the eviction policy.

The class should accept an EvictionPolicy interface, which defines how an entry is selected for removal when the cache reaches capacity.

Requirements

  1. put(key, value): Insert or update a key-value pair. If capacity is exceeded, invoke the policy to evict an item.
  2. get(key): Return the value associated with the key and update the policy's metadata (e.g., frequency or recency).
  3. You must implement two policies: LRU (Least Recently Used) and LFU (Least Frequently Used).

Interface

class CustomCache:
    def __init__(self, capacity: int, policy: str):
        pass

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

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

Constraints

  • capacity is a positive integer.
  • key and value are integers.
  • Ensure get operations on non-existent keys return -1.

Example

# Using LRU
cache = CustomCache(2, "LRU")
cache.put(1, 1)
cache.put(2, 2)
cache.get(1)    # returns 1
cache.put(3, 3) # evicts key 2
cache.get(2)    # returns -1

Hints

Hint 1

Use a hash map to map keys to their corresponding nodes to ensure O(1) retrieval.

Hint 2

For LRU, maintain a doubly linked list where the head represents the most recently accessed items and the tail the least recent.

Hint 3

For LFU, consider using a nested data structure: a hash map of frequencies, where each frequency maps to a set or doubly linked list of keys.

Follow-up questions

  • How would you modify this cache to handle concurrency if multiple threads perform reads and writes simultaneously?
  • If the cache needs to persist data to disk, what architectural changes would you propose to minimize latency?
  • How would you handle 'LFU frequency ties' (when multiple keys have the same minimum frequency)?