Katkat

Implement an LFU cache with O(1) operations

SalesforceSoftware Engineer · Onsite

Last updated

This problem tests your ability to maintain multiple layers of linked data structures to achieve O(1) time complexity for both access and eviction in an LFU (Least Frequently Used) cache.

Problem: LFU Cache

Design and implement a Least Frequently Used (LFU) cache. The cache should support the following operations with O(1) average time complexity:

  • get(key): Return the value of the key if it exists, otherwise return -1. Increment the frequency of the accessed key.
  • put(key, value): Update the value if the key exists or insert the key if it does not. If the cache reaches capacity, remove the item with the lowest frequency. If there is a tie in frequency, remove the item that was least recently used.

Interface

class LFUCache:
    def __init__(self, capacity: int):
        # Initialize cache with capacity
        pass

    def get(self, key: int) -> int:
        # Returns value if key exists, else -1
        pass

    def put(self, key: int, value: int) -> None:
        # Inserts or updates key
        pass

Constraints

  • 0 <= capacity <= 10^4
  • 0 <= key <= 10^5
  • 0 <= value <= 10^9

Examples

Example 1:

cache = LFUCache(2)
cache.put(1, 1)
cache.put(2, 2)
cache.get(1)    # returns 1, cache state: {1: freq 2, 2: freq 1}
cache.put(3, 3) # evicts key 2 (freq 1), cache state: {1: freq 2, 3: freq 1}
cache.get(2)    # returns -1

Edge Cases

  • Capacity is 0.
  • Accessing keys that do not exist.
  • Repeatedly updating an existing key's value.

Hints

Hint 1

Use a hash map to map keys to their values and frequency counts.

Hint 2

Maintain a hash map where keys are frequencies and values are doubly linked lists of keys, allowing O(1) insertion and deletion for a given frequency.

Hint 3

Keep track of the minimum frequency currently in the cache to quickly identify which frequency list to evict from.

Follow-up questions

  • How would your implementation change if you needed to support TTL (Time To Live) for each cache entry?
  • What are the trade-offs between an LFU cache and an LRU (Least Recently Used) cache in a high-concurrency system?
  • How would you adapt this design to work across multiple distributed servers?