HomeLeetcode146. LRU Cache - Leetcode Solutions

146. LRU Cache – Leetcode Solutions

Description

Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

Implement the LRUCache class:

  • LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
  • int get(int key) Return the value of the key if the key exists, otherwise return -1.
  • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.

The functions get and put must each run in O(1) average time complexity.

Examples:

Example 1:

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

Explanation
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1);    // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2);    // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1);    // return -1 (not found)
lRUCache.get(3);    // return 3
lRUCache.get(4);    // return 4

Solution in Python

To implement a Least Recently Used (LRU) Cache in Python, we can use an OrderedDict from the collections module. The OrderedDict maintains the order in which keys are inserted, which helps us efficiently move the recently accessed keys to the end and keep track of the least recently used keys.

Python
from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity: int):
        """
        Initialize the LRU cache with positive size capacity.
        """
        self.cache = OrderedDict()
        self.capacity = capacity

    def get(self, key: int) -> int:
        """
        Return the value of the key if the key exists, otherwise return -1.
        """
        if key not in self.cache:
            return -1
        
        # Move the accessed key to the end to mark it as recently used
        self.cache.move_to_end(key)
        return self.cache[key]

    def put(self, key: int, value: int) -> None:
        """
        Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache.
        If the number of keys exceeds the capacity from this operation, evict the least recently used key.
        """
        if key in self.cache:
            # Update the value and move the key to the end to mark it as recently used
            self.cache.move_to_end(key)
        self.cache[key] = value
        
        if len(self.cache) > self.capacity:
            # Pop the first item (least recently used) if the capacity is exceeded
            self.cache.popitem(last=False)

Explanation:

  1. Initialization (__init__ method):
    • We initialize the cache using an OrderedDict.
    • We store the capacity of the cache.
  2. Get Method (get method):
    • If the key is not in the cache, return -1.
    • If the key is found, move the key to the end to mark it as recently used and return its value.
  3. Put Method (put method):
    • If the key already exists in the cache, update its value and move it to the end to mark it as recently used.
    • Add or update the key-value pair in the cache.
    • If the cache exceeds the capacity after insertion, remove the least recently used item. The popitem(last=False) method removes the first item in the OrderedDict, which is the least recently used.

This approach ensures that both get and put operations run in O(1) average time complexity, which is a requirement of the problem. The use of OrderedDict helps in maintaining the order of elements based on their usage efficiently.

Solution in Javascript

JavaScript
/**
 * LRUCache class that simulates an LRU (Least Recently Used) cache.
 * @param {number} capacity - The maximum number of items the cache can hold.
 */
var LRUCache = function(capacity) {
    this.capacity = capacity;
    this.cache = new Map();
};

/** 
 * Gets the value of the key if the key exists in the cache.
 * Moves the accessed key to the end to mark it as recently used.
 * @param {number} key
 * @return {number} - The value of the key, or -1 if the key does not exist.
 */
LRUCache.prototype.get = function(key) {
    if (!this.cache.has(key)) {
        return -1; // Key does not exist
    }
    const value = this.cache.get(key);
    // Move the key to the end to mark it as recently used
    this.cache.delete(key);
    this.cache.set(key, value);
    return value;
};

/** 
 * Updates the value of the key if the key exists. Otherwise, adds the key-value pair to the cache.
 * If the number of keys exceeds the capacity, evicts the least recently used key.
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    if (this.cache.has(key)) {
        // If the key already exists, delete it so we can re-insert it as the most recently used
        this.cache.delete(key);
    }
    this.cache.set(key, value);
    
    // If the cache exceeds the capacity, remove the least recently used item
    if (this.cache.size > this.capacity) {
        // keys().next().value retrieves the first item's key in the Map
        const oldestKey = this.cache.keys().next().value;
        this.cache.delete(oldestKey);
    }
};

Solution in Java

Java
import java.util.HashMap;

class LRUCache {

    // Node class to represent each entry in the cache
    private class Node {
        int key;
        int value;
        Node prev;
        Node next;

        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    private int capacity; // Maximum capacity of the cache
    private HashMap<Integer, Node> map; // HashMap to store the keys and nodes
    private Node head; // Dummy head of the doubly linked list
    private Node tail; // Dummy tail of the doubly linked list

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.map = new HashMap<>();
        this.head = new Node(-1, -1); // Initialize dummy head
        this.tail = new Node(-1, -1); // Initialize dummy tail
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1; // Return -1 if the key is not found
        }
        Node node = map.get(key);
        remove(node); // Remove the node from its current position
        insertToHead(node); // Insert the node to the head (most recently used position)
        return node.value;
    }
    
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            // If the key exists, update the value and move it to the head
            Node node = map.get(key);
            node.value = value;
            remove(node);
            insertToHead(node);
        } else {
            // If the key does not exist, create a new node
            Node newNode = new Node(key, value);
            if (map.size() >= capacity) {
                // If the cache is full, remove the least recently used item
                Node lru = tail.prev;
                remove(lru);
                map.remove(lru.key);
            }
            // Add the new node to the cache and insert it to the head
            map.put(key, newNode);
            insertToHead(newNode);
        }
    }

    // Helper method to remove a node from the doubly linked list
    private void remove(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    // Helper method to insert a node to the head of the doubly linked list
    private void insertToHead(Node node) {
        node.next = head.next;
        node.prev = head;
        head.next.prev = node;
        head.next = node;
    }
}

Solution in C#

C#
using System;
using System.Collections.Generic;

public class LRUCache {

    // Node class to represent each entry in the cache
    private class Node {
        public int Key;
        public int Value;
        public Node Prev;
        public Node Next;
        
        public Node(int key, int value) {
            this.Key = key;
            this.Value = value;
        }
    }

    private int capacity; // Maximum capacity of the cache
    private Dictionary<int, Node> cache; // Dictionary to store key-node pairs
    private Node head; // Dummy head of the doubly linked list
    private Node tail; // Dummy tail of the doubly linked list

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new Dictionary<int, Node>();
        this.head = new Node(0, 0); // Initialize dummy head
        this.tail = new Node(0, 0); // Initialize dummy tail
        head.Next = tail;
        tail.Prev = head;
    }

    public int Get(int key) {
        if (!cache.ContainsKey(key)) {
            return -1; // Return -1 if the key is not found
        }
        Node node = cache[key];
        Remove(node); // Remove the node from its current position
        InsertToHead(node); // Insert the node to the head (most recently used position)
        return node.Value;
    }

    public void Put(int key, int value) {
        if (cache.ContainsKey(key)) {
            // If the key exists, update the value and move it to the head
            Node node = cache[key];
            node.Value = value;
            Remove(node);
            InsertToHead(node);
        } else {
            // If the key does not exist, create a new node
            Node newNode = new Node(key, value);
            if (cache.Count >= capacity) {
                // If the cache is full, remove the least recently used item
                Node lru = tail.Prev;
                Remove(lru);
                cache.Remove(lru.Key);
            }
            // Add the new node to the cache and insert it to the head
            cache[key] = newNode;
            InsertToHead(newNode);
        }
    }

    // Helper method to remove a node from the doubly linked list
    private void Remove(Node node) {
        node.Prev.Next = node.Next;
        node.Next.Prev = node.Prev;
    }

    // Helper method to insert a node to the head of the doubly linked list
    private void InsertToHead(Node node) {
        node.Next = head.Next;
        node.Prev = head;
        head.Next.Prev = node;
        head.Next = node;
    }
}

Subscribe
Notify of

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

Popular