Logo for ammarahmed.ca
Medium
Oct 06, 2025
#linked-list
#OOD

146. LRU Cache

Problem

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.

        Example 1:

        1Input 2["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] 3[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] 4Output 5[null, null, null, 1, null, -1, null, -1, 3, 4] 6 7Explanation 8LRUCache lRUCache = new LRUCache(2); 9lRUCache.put(1, 1); // cache is {1=1} 10lRUCache.put(2, 2); // cache is {1=1, 2=2} 11lRUCache.get(1); // return 1 12lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3} 13lRUCache.get(2); // returns -1 (not found) 14lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3} 15lRUCache.get(1); // return -1 (not found) 16lRUCache.get(3); // return 3 17lRUCache.get(4); // return 4 18

        Constraints:

        • 1 <= capacity <= 3000
          • 0 <= key <= 104
            • 0 <= value <= 105
              • At most 2 * 105 calls will be made to get and put.

                Approach

                To solve this problem optimally, we can use a doubly linked list in which we keep track of the left and right (head and tail) of the list for our LRU and MRU (most recently used), respectively.

                Since we want O(1) retrieval, we'll use a hash map to store the keys alongside a pointer to a node in the list where the key and value will be stored.

                When we retrieve an element, we want to update our list to reflect that the retrieval is now the MRU so what we do is remove that specific node from the list and then re-insert at the right (tail) for it to become the MRU.

                When we want to insert an element, we create a new node for our list, insert it at the right (MRU). Then, we check if our capacity has been exceeded and remove the node at the left (LRU) and delete the corresponding key from the hashmap.

                Complexity

                Time: O(1)

                Removing and inserting into the list when we have pointers for the head and tail is constant.

                Space: O(cap)

                We create a hashmap and a doubly linked list that will have a maximum size of capacity.

                Solution

                1type Node struct { 2 Key int 3 Val int 4 Next *Node 5 Prev *Node 6} 7 8type LRUCache struct { 9 left *Node 10 right *Node 11 capacity int 12 cache map[int]*Node 13} 14 15func Constructor(capacity int) LRUCache { 16 // Left=LRU, right=MRU 17 left := &Node{} 18 right := &Node{} 19 20 // Connect them to start 21 left.Next = right 22 right.Prev = left 23 return LRUCache{ 24 capacity: capacity, 25 left: left, 26 right: right, 27 cache: make(map[int]*Node), 28 } 29} 30 31// Remove the node 32func (this *LRUCache) remove(node *Node) { 33 prev, next := node.Prev, node.Next 34 prev.Next = next 35 next.Prev = prev 36} 37 38// Insert this node at right (i.e. in between right.prev and right) 39func (this *LRUCache) insert(node *Node) { 40 prev, next := this.right.Prev, this.right 41 prev.Next = node 42 next.Prev = node 43 node.Next = next 44 node.Prev = prev 45} 46 47func (this *LRUCache) Get(key int) int { 48 if node, exists := this.cache[key]; exists { 49 // Remove this node 50 this.remove(node) 51 // Insert node at right (MRU) 52 this.insert(node) 53 return node.Val 54 } 55 return -1 56} 57 58func (this *LRUCache) Put(key int, value int) { 59 if node, exists := this.cache[key]; exists { 60 // Remove this node (already exists) 61 this.remove(node) 62 } 63 this.cache[key] = &Node{Key: key, Val: value} 64 // Insert this node (this.cache[key]) at MRU 65 node, _ := this.cache[key] 66 this.insert(node) 67 68 if len(this.cache) > this.capacity { 69 // Remove the LRU and delete from hash map 70 lru := this.left.Next 71 this.remove(lru) 72 delete(this.cache, lru.Key) 73 } 74} 75