Rakesh PeddamalluAboutProjectsBlogLeetcode Company WiseContact

Suggested Problems

Leetcode - 146. LRU Cache

Problem 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.

 

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

 

Constraints:

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

Example Test Cases

  • ["LRUCache","put","put","get","put","get","put","get","get","get"]
  • [[2],[1,1],[2,2],[1],[3,3],[2],[4,4],[1],[3],[4]]

Before diving into Solution go through the hints given in the problem

Solution

๐Ÿ” Building an LRU Cache in JavaScript (The Right Way)

Imagine you're building a browser, an in-memory database, or any service where you want to store recently accessed items and automatically discard the least-used ones. You need an LRU Cache โ€“ a smart data structure that gives you:

  • O(1) access to items
  • Automatic eviction of the least recently used item when capacity is exceeded

In this post, weโ€™ll build an efficient LRU Cache using JavaScript, a Map, and a Doubly Linked List. Let's dive in! ๐Ÿ‘‡


๐Ÿš€ What is an LRU Cache?

LRU stands for Least Recently Used. The idea is simple:

  • Store a fixed number of key-value pairs
  • When you get or put, the item becomes the most recently used
  • If the cache exceeds capacity, evict the least recently used item

This keeps your cache optimized, fast, and memory-efficient.


๐Ÿง  What Weโ€™ll Use

Weโ€™ll combine two data structures:

  1. Map โ€“ stores key โ†’ node (for O(1) lookups)
  2. Doubly Linked List โ€“ tracks the order of usage from least recent (left) to most recent (right)

We also use two dummy nodes (left and right) to make insertions/removals cleaner without edge-case checks.


๐Ÿ”ง Our Custom Helper Methods

To keep our logic clean, we write two internal utility methods.


1. _remove(node) โ€“ Remove Node from List

1LRUCache.prototype._remove = function (node) {
2    node.prev.next = node.next;
3    node.next.prev = node.prev;
4};

Image description

โœ… Purpose: Detach a node from the doubly linked list.
๐Ÿ“ When it's used:

  • When an existing key is accessed (to reinsert it as most recent)
  • When evicting the least recently used node

2. _insert(node) โ€“ Insert Node at MRU End

1LRUCache.prototype._insert = function (node) {
2    const prev = this.right.prev;
3    const next = this.right;
4    prev.next = node;
5    node.prev = prev;
6    node.next = next;
7    next.prev = node;
8};

Image description

โœ… Purpose: Always insert a node just before the right dummy (i.e., mark it as most recently used)
๐Ÿ“ When it's used:

  • After inserting a new node
  • After accessing an existing node (to update its position)

๐Ÿคซ Why the _ prefix in _remove and _insert?

Itโ€™s just a naming convention. It signals that these methods are internal helpers and shouldnโ€™t be used outside the class. JavaScript doesn't enforce private methods in traditional ES5 syntax, so _ helps you communicate intent.


๐Ÿงฑ Full LRU Cache Implementation

Hereโ€™s the complete working code ๐Ÿ‘‡ Image description

Image description

1// Node class for the doubly linked list
2class Node {
3    constructor(key, value) {
4        this.key = key;
5        this.value = value;
6        this.prev = null;
7        this.next = null;
8    }
9}
10
11
12
13var LRUCache = function (capacity) {
14    this.capacity = capacity;
15    this.cache = new Map(); // key โ†’ node
16
17    // Dummy nodes: left = LRU end, right = MRU end
18    this.left = new Node(0, 0);
19    this.right = new Node(0, 0);
20    this.left.next = this.right;
21    this.right.prev = this.left;
22};
23
24// Removes a node from the doubly linked list
25LRUCache.prototype._remove = function (node) {
26    node.prev.next = node.next;
27    node.next.prev = node.prev;
28};
29
30// Inserts a node at the MRU (right) end
31LRUCache.prototype._insert = function (node) {
32    const prev = this.right.prev;
33    const next = this.right;
34    prev.next = node;
35    node.prev = prev;
36    node.next = next;
37    next.prev = node;
38};
39
40// Get a value by key and update it to be most recent
41LRUCache.prototype.get = function (key) {
42    if (this.cache.has(key)) {
43        const node = this.cache.get(key);
44        this._remove(node);  // remove from current position
45        this._insert(node);  // re-insert as most recent
46        return node.value;
47    }
48    return -1;
49};
50
51// Insert or update a key-value pair
52LRUCache.prototype.put = function (key, value) {
53    if (this.cache.has(key)) {
54        this._remove(this.cache.get(key));
55    }
56
57    const node = new Node(key, value);
58    this.cache.set(key, node);
59    this._insert(node);
60
61    // Evict LRU if capacity exceeded
62    if (this.cache.size > this.capacity) {
63        const lru = this.left.next;  // least recently used node
64        this._remove(lru);
65        this.cache.delete(lru.key);
66    }
67};

๐Ÿง  Time and Space Complexity

OperationTime ComplexityWhy
getO(1)Map lookup + pointer updates
putO(1)Map insert + pointer operations
SpaceO(N)Stores up to capacity nodes

โœ… Summary

By combining a Map and a Doubly Linked List, we achieve:

  • O(1) performance for both get and put
  • Clean and predictable memory eviction
  • Modular, readable code with private helper functions

This structure is battle-tested and used across real-world systems โ€” from operating system memory managers to Redis and beyond.


Support My Blog โค๏ธ

If this blog helped you, consider a small contribution!

Your support keeps this going! ๐Ÿš€๐Ÿ™

Published by: Rakesh Reddy Peddamallu

Top Comments (0)