Skip to content
AI360Xpert
Beta

LFU Cache

Problem in Plain English

Build a fixed-capacity cache that evicts the least frequently used key, breaking frequency ties by least recent use. Both get and put must run in constant time.

Problem Statement

Design a Least Frequently Used (LFU) cache with a fixed capacity. get(key) returns the stored value and raises the key's frequency, or returns -1 when absent without changing the cache. put(key, value) inserts a new key at frequency 1, or updates an existing key and raises its frequency exactly once. Both successful reads and updates count as uses and refresh recency.

Before inserting into a full cache, evict the key with the smallest frequency; among tied keys, evict the least recently used. A capacity of 0 stores nothing. The required design supports each operation in expected O(1)O(1) time under constant-time hash-map operations; a scanning baseline below explains the bottleneck.

Constraints

  • 0≤capacity≤1040 \leq capacity \leq 10^4
  • 0≤key≤1050 \leq key \leq 10^5
  • 0≤value≤1090 \leq value \leq 10^9
  • At most 2×1052 \times 10^5 calls are made to get and put.

Examples

Example 1

Input
["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, 3, null, -1, 3, 4]

The constructor and writes return null. Reading key 1 raises it to frequency 2, so inserting key 3 evicts key 2 at frequency 1. Reading absent key 2 returns -1 without changing state. Reading key 3 creates a frequency-2 tie; key 1 was used earlier, so inserting key 4 evicts key 1. The final reads return -1, 3, and 4.

Example 2

Input
["LFUCache", "put", "get"] [[0], [1, 1], [1]]
Output
[null, null, -1]

A zero capacity stores nothing, so the later get misses and returns -1.

Example 3

Input
["LFUCache", "put", "put", "put", "put", "get", "get", "get"] [[2], [0, 0], [1, 10], [0, 7], [2, 20], [1], [0], [2]]
Output
[null, null, null, null, null, -1, 7, 20]

Updating key 0 changes its value from 0 to 7 and increases its frequency from 1 to 2. Inserting key 2 therefore evicts key 1 at frequency 1, even though key 0 was inserted first. The miss returns -1; the survivors return their current values 7 and 20. Key and value zero are valid and must not be treated as absence.

Intuition

Frequency answers how often a key has been used; recency answers which tied key was used least recently. In Example 1, reading key 1 raises its frequency to 2, so inserting key 3 evicts key 2 at frequency 1. After reading key 3, both survivors have frequency 2, but key 1 is older and loses the next eviction tie. Keep a separate recency list for each frequency and remember the smallest occupied frequency: together they identify the victim without scanning. Updating an existing value is also a use, while a missed read changes neither frequency nor recency.

Approaches

Scan For Eviction

Solution Details

Reveal Scan For Eviction: intuition, complexity, and code

Hints

Hint 1
A plain map finds keys instantly, but choosing the eviction victim by scanning every entry costs linear time.
Hint 2
Group keys by frequency so the eviction candidate always sits in the group with the smallest count.
Hint 3
Track the minimum frequency explicitly, keep each group in recency order with sentinel nodes, and move a key to the next group on every touch.

Edge Cases

  • Capacity 0: every put is a no-op and every get returns -1; there is no minimum occupied frequency.
  • Capacity 1: repeatedly reading the only key must leave exactly one nonempty frequency group. A new key still evicts that key regardless of how large its frequency became.
  • An existing-key put is a successful use: update its value, increment frequency, and refresh recency without evicting any key, even when full.
  • If several residents share the minimum frequency, eviction removes the oldest successful use in that group. A missed read must not refresh recency.
  • A large gap between occupied frequencies is valid. Only promotion of the last minimum node allows minFreq += 1; eviction instead relies on the incoming frequency-1 node.
  • Key 0, value 0, and value 1000000000 are legal. Use map membership rather than truthiness to distinguish a stored zero from an absent key.

Common Mistakes and Interview Tips

  • Updating the value on an existing-key put without increasing frequency and refreshing recency violates the use-count contract.
  • Incrementing minFreq whenever any group empties is wrong; promotion changes it only if that group was the current minimum.
  • Retaining empty frequency groups after promotion or eviction leaks one sentinel list per historical frequency; capacity 1 with many reads exposes this.
  • Trying to advance minFreq by one after eviction can land on an empty frequency gap. Reset it to 1 when inserting the new node instead.
  • Removing an eviction victim only from the linked list leaves a ghost key mapping. Each resident must appear once in both structures.
  • Appending a touched node at the LRU end reverses the tie-breaking rule. Insert at the MRU front and evict from the tail.
  • Interview tip: distinguish frequency from global recency, state the group and minimum invariants, and explain why removing the last minimum node during promotion gives a known next minimum.

Key Takeaway

When priority changes by exactly one on a use, a key map plus ordered frequency buckets can maintain ranked eviction without a global sort. A known next rank handles promotion; an inserted rank-1 item restores the minimum after eviction. Reclaim empty buckets so the structure remains bounded by live capacity.