Algorithm-Day36-LRU-Cache-lc-146

🧩 Problem Description

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

Implement the LRUCache class:

  • LRUCache(int capacity) initializes the cache with a positive size capacity.
  • int get(int key) returns the value of the key if it exists, otherwise returns -1.
  • void put(int key, int value) updates the value of the key. If the number of keys exceeds the capacity, evict the least recently used key.

πŸ’¬ Example

1
2
3
4
5
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]

πŸ’‘ Intuition

To achieve O(1) time complexity for both get and put, use:

  • A HashMap to store key β†’ node
  • A Doubly Linked List to track the usage order
    • Most recently used at the front
    • Least recently used at the end

When a key is accessed:

  • Move it to the front of the list
    When inserting:
  • If at capacity, remove the tail (least recently used)

πŸ”’ Java Code (Custom Doubly Linked List)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class LRUCache {

class Node {
int key, value;
Node prev, next;
Node(int k, int v) {
key = k;
value = v;
}
}

private final int capacity;
private Map<Integer, Node> cache;
private Node head, tail;

public LRUCache(int capacity) {
this.capacity = capacity;
cache = new HashMap<>();

// Dummy head and tail
head = new Node(0, 0);
tail = new Node(0, 0);

head.next = tail;
tail.prev = head;
}

public int get(int key) {
if (!cache.containsKey(key)) return -1;

Node node = cache.get(key);
moveToHead(node);
return node.value;
}

public void put(int key, int value) {
if (cache.containsKey(key)) {
Node node = cache.get(key);
node.value = value;
moveToHead(node);
} else {
if (cache.size() == capacity) {
Node lru = tail.prev;
remove(lru);
cache.remove(lru.key);
}

Node newNode = new Node(key, value);
cache.put(key, newNode);
addToHead(newNode);
}
}

// Doubly Linked List helpers
private void remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}

private void addToHead(Node node) {
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}

private void moveToHead(Node node) {
remove(node);
addToHead(node);
}
}

⏱ Time and Space Complexity

  • Time Complexity:
    • get: O(1)
    • put: O(1)
  • Space Complexity: O(capacity)

🧠 Summary

  • Combines a HashMap (for O(1) access) and a Doubly Linked List (for O(1) insert/delete).
  • A great example of system design + data structure synergy.
  • Common interview favorite!

Know this one cold β€” it’s the basis for many real-world cache systems (like Redis, in-memory caching, etc.)