LFU Cache in Python

A Least Frequently Used (LFU) cache is a data structure that removes the least frequently accessed item when the cache reaches its maximum capacity. Python's collections.OrderedDict helps track both frequency and insertion order for efficient implementation.

LFU Cache Operations

The LFU cache supports two main operations:

  • get(key) - Returns the value if the key exists, otherwise returns -1

  • put(key, value) - Inserts or updates a key-value pair

When capacity is reached, the cache removes the least frequently used element before inserting a new one.

Implementation Strategy

The implementation uses two data structures:

  • node_for_key - Maps keys to their values and frequencies

  • node_for_freq - Groups keys by frequency using OrderedDict for insertion order

  • least_freq - Tracks the minimum frequency for efficient eviction

Complete Implementation

import collections

class LFUCache:
    def __init__(self, capacity):
        self.remain = capacity
        self.least_freq = 1
        self.node_for_freq = collections.defaultdict(collections.OrderedDict)
        self.node_for_key = dict()
    
    def _update(self, key, value):
        # Get current frequency and remove from current frequency group
        _, freq = self.node_for_key[key]
        self.node_for_freq[freq].pop(key)
        
        # If least frequency group becomes empty, increment least_freq
        if len(self.node_for_freq[self.least_freq]) == 0:
            self.least_freq += 1
        
        # Add to next frequency group
        self.node_for_freq[freq + 1][key] = (value, freq + 1)
        self.node_for_key[key] = (value, freq + 1)
    
    def get(self, key):
        if key not in self.node_for_key:
            return -1
        
        value = self.node_for_key[key][0]
        self._update(key, value)
        return value
    
    def put(self, key, value):
        if self.remain == 0:
            return
            
        if key in self.node_for_key:
            # Update existing key
            self._update(key, value)
        else:
            # Insert new key with frequency 1
            self.node_for_key[key] = (value, 1)
            self.node_for_freq[1][key] = (value, 1)
            
            if self.remain == 0:
                # Remove least frequently used item
                removed = self.node_for_freq[self.least_freq].popitem(last=False)
                self.node_for_key.pop(removed[0])
            else:
                self.remain -= 1
            
            self.least_freq = 1

# Example usage
cache = LFUCache(2)
cache.put(1, 1)
cache.put(2, 2)
print(cache.get(1))    # Returns 1, frequency of key 1 becomes 2
cache.put(3, 3)        # Evicts key 2 (least frequent)
print(cache.get(2))    # Returns -1 (key 2 was evicted)
cache.put(4, 4)        # Evicts key 3 (least frequent)
print(cache.get(1))    # Returns 1
print(cache.get(3))    # Returns -1 (key 3 was evicted)
print(cache.get(4))    # Returns 4
1
-1
1
-1
4

How It Works

The algorithm maintains frequency groups using OrderedDict to preserve insertion order within each frequency level. When a key is accessed:

  • Its frequency increases by 1

  • It moves to the next frequency group

  • If the current least frequency group becomes empty, least_freq is incremented

When eviction is needed, the first item from the least frequency group is removed using FIFO order.

Time Complexity

Operation Time Complexity Space Complexity
get() O(1) O(capacity)
put() O(1) O(capacity)

Conclusion

The LFU cache efficiently manages memory by tracking access frequencies using OrderedDict. Both get and put operations run in O(1) time, making it suitable for high-performance applications requiring intelligent caching strategies.

Updated on: 2026-03-25T09:09:20+05:30

3K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements