w3resource

LRU Cache Implementation: Optimizing Memory with Python Dictionary Tracking


88. Dictionary-based LRU Cache

Write a Python program to implement an LRU (Least Recently Used) cache using dictionaries. The cache should have a fixed capacity and remove the least recently used items when full. Implement get and put methods with O(1) time complexity.

Solution:

Python Code:

# Define a class for implementing an LRU (Least Recently Used) cache.
class LRUCache:
    """
    An LRU (Least Recently Used) cache implementation using dictionaries.
    Has a fixed capacity and removes the least recently used items when full.
    """
    # Initialize the LRU Cache with a specified capacity, an empty dictionary for storage, and an order counter.
    def __init__(self, capacity):
        self.capacity = capacity  # Maximum number of items the cache can hold.
        self.cache = {}  # Dictionary to store key-value pairs along with their access order: key -> (value, order).
        self.order = 0  # Counter to track the order in which items are accessed or added.

    # Define the behavior for retrieving a value from the cache.
    def get(self, key):
        """
        Get a value from the cache and update its access order.
        
        Returns:
            The value if found, otherwise -1
        """
        # If the key is not in the cache, return -1 to indicate it's not found.
        if key not in self.cache:
            return -1
        
        # Retrieve the value and its current access order from the cache.
        value, _ = self.cache[key]
        # Increment the global order counter and update the access order for this key.
        self.order += 1
        self.cache[key] = (value, self.order)
        
        # Return the value associated with the key.
        return value

    # Define the behavior for adding or updating a key-value pair in the cache.
    def put(self, key, value):
        """
        Add or update a key-value pair in the cache.
        If the cache is full, remove the least recently used item.
        """
        # Increment the global order counter to reflect this operation.
        self.order += 1
        
        # If the key already exists in the cache, update its value and access order.
        if key in self.cache:
            self.cache[key] = (value, self.order)
            return
        
        # If the cache has reached its capacity, remove the least recently used item.
        if len(self.cache) >= self.capacity:
            # Find the key with the smallest access order (least recently used).
            lru_key = min(self.cache.items(), key=lambda x: x[1][1])[0]
            # Remove the least recently used key from the cache.
            del self.cache[lru_key]
        
        # Add the new key-value pair to the cache with the current access order.
        self.cache[key] = (value, self.order)

# Example usage of LRU Cache

# Create an instance of LRUCache with a capacity of 2.
cache = LRUCache(2)  # Capacity is set to 2.

# Add key-value pairs to the cache.
cache.put(1, 1)  # Cache now contains {1: (1, 1)}.
cache.put(2, 2)  # Cache now contains {1: (1, 1), 2: (2, 2)}.

# Retrieve the value for key 1 and update its access order.
print(cache.get(1))  # Output: 1 (Cache now contains {2: (2, 2), 1: (1, 3)}).

# Add a new key-value pair, which evicts the least recently used item (key 2).
cache.put(3, 3)  # Cache now contains {1: (1, 3), 3: (3, 4)}.
print(cache.get(2))  # Output: -1 (Key 2 is not found).

# Retrieve the value for key 3 and update its access order.
print(cache.get(3))  # Output: 3 (Cache now contains {1: (1, 3), 3: (3, 5)}).

# Add another key-value pair, which evicts the least recently used item (key 1).
cache.put(4, 4)  # Cache now contains {3: (3, 5), 4: (4, 6)}.
print(cache.get(1))  # Output: -1 (Key 1 is not found).

# Retrieve the values for keys 3 and 4, updating their access orders.
print(cache.get(3))  # Output: 3 (Cache now contains {4: (4, 6), 3: (3, 7)}).
print(cache.get(4))  # Output: 4 (Cache now contains {3: (3, 7), 4: (4, 8)}).

Output:

1
-1
3
-1
3
4

Explanation of Each Line:

  • Class Definition : Defines the LRUCache class to implement an LRU (Least Recently Used) cache.
  • Docstring : Provides a description of the class and its functionality.
  • Initialization : Initializes the cache with a fixed capacity, an empty dictionary (self.cache), and an order counter (self.order) to track access order.
  • Get Method : Implements the get method to retrieve a value from the cache and update its access order.
  • Key Not Found Check : Checks if the requested key exists in the cache; returns -1 if not found.
  • Update Access Order : Retrieves the value and updates the access order for the key by incrementing the global order counter.
  • Return Value : Returns the value associated with the key after updating its access order.
  • Put Method : Implements the put method to add or update a key-value pair in the cache.
  • Increment Order Counter : Increments the global order counter to reflect the current operation.
  • Update Existing Key : If the key already exists, updates its value and access order without evicting any items.
  • Eviction Logic : If the cache exceeds its capacity, identifies and removes the least recently used item based on the access order.
  • Find Least Recently Used : Uses the min function to find the key with the smallest access order.
  • Remove LRU Key : Deletes the least recently used key from the cache.
  • Add New Item : Adds the new key-value pair to the cache with the current access order.
  • Example Usage : Demonstrates how to use the LRUCache class with example operations.
  • Create Cache Instance : Creates an LRUCache instance with a capacity of 2.
  • Add Key-Value Pairs : Adds key-value pairs to the cache.
  • Retrieve Value : Retrieves the value for a key and updates its access order.
  • Evict LRU Item : Adds a new key-value pair, evicting the least recently used item when the cache is full.
  • Check Evicted Key : Verifies that the evicted key is no longer in the cache.
  • Retrieve Values : Retrieves values for remaining keys, updating their access orders.

Explanation - Dictionary-based LRU Cache

  • Concept: Implement a Least Recently Used (LRU) cache with fixed capacity using dictionaries.
  • Challenge: Achieve O(1) time complexity for operations while tracking usage order.
  • Key Skills:
    • Algorithm design
    • Efficiency optimization
    • Cache management techniques
  • Applications:
    • Browser caching
    • Database query caching
    • API response caching
    • Memoization with limited memory
  • Benefits:
    • Optimizes memory usage by removing least used items
    • Provides fast access to frequently used data
    • Balances memory constraints with performance needs

For more Practice: Solve these Related Problems:

  • Write a Python function to implement a dictionary-based LFU (Least Frequently Used) cache.
  • Write a Python program to implement an LRU cache with different expiration times for different types of data.
  • Write a Python function to implement an LRU cache that persists to disk when memory is full.
  • Write a Python function to create an LRU cache that supports time-based and frequency-based eviction strategies.

Python Code Editor:

Previous: Bidirectional Dictionary.
Next: Dictionary Serialization with Circular References.

What is the difficulty level of this exercise?

Test your Programming skills with w3resource's quiz.



Follow us on Facebook and Twitter for latest update.