Skip to content

Commit 0711fdf

Browse files
committed
Solution for LRU Cache
1 parent 67de492 commit 0711fdf

File tree

2 files changed

+191
-0
lines changed

2 files changed

+191
-0
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# 146. LRU Cache
2+
Difficulty: Medium
3+
Category: HashMap, Doubly Linked List, Design
4+
Leetcode Link: [Problem Link](https://leetcode.com/problems/lru-cache/)
5+
6+
---
7+
8+
## 📝 Introduction
9+
10+
Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
11+
12+
Implement the LRUCache class:
13+
- LRUCache(int capacity) initializes the LRU cache with a positive size capacity.
14+
- int get(int key) returns the value of the key if the key exists, otherwise returns -1.
15+
- void put(int key, int value) updates the value of the key if it exists. Otherwise, adds the key-value pair to the cache. If the number of keys exceeds the capacity, evict the least recently used key.
16+
17+
Both get and put must run in O(1) average time complexity.
18+
19+
---
20+
21+
## 💡 Approach & Key Insights
22+
23+
To achieve O(1) time complexity for both get and put operations:
24+
- Use a HashMap to store key → pointer to node in a Doubly Linked List (DLL).
25+
- The DLL keeps track of the most recently used order, with:
26+
- Head: Most recently used.
27+
- Tail: Least recently used.
28+
- When a key is accessed or added:
29+
- Move its node to the front (right after head).
30+
- If capacity is exceeded, remove node from the end (before tail).
31+
32+
---
33+
34+
## 🛠️ Breakdown of Approaches
35+
36+
### 1️⃣ Brute Force / Naive Approach
37+
38+
Explanation:
39+
Use a LinkedList and linear search to implement put and get.
40+
Too slow for O(1) requirement. Every access or insert may require O(N) time for searching.
41+
42+
Time Complexity: O(N) per operation
43+
Space Complexity: O(N) for storage
44+
45+
Example/Dry Run:
46+
get(1) — Scan entire list to find key = 1 → O(N)
47+
48+
---
49+
50+
### 2️⃣ Optimized Approach (HashMap + DLL)
51+
52+
Explanation:
53+
Use:
54+
- HashMap<int, Node*> to access nodes in O(1).
55+
- Doubly Linked List to maintain LRU order.
56+
57+
Steps:
58+
- get(key):
59+
- If key exists → move node to head → return value.
60+
- Else → return -1.
61+
- put(key, value):
62+
- If key exists → update value → move node to head.
63+
- If not → insert new node → add to head.
64+
- If capacity exceeded → remove node from tail → erase from map.
65+
66+
Time Complexity: O(1) for both get and put
67+
Space Complexity: O(capacity)
68+
69+
Example/Dry Run:
70+
Input:
71+
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
72+
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
73+
Output: [null, null, null, 1, null, -1, null, -1, 3, 4]
74+
75+
---
76+
77+
### 3️⃣ Best / Final Optimized Approach (if applicable)
78+
79+
Same as optimized. No better than O(1) per operation.
80+
81+
---
82+
83+
## 📊 Complexity Analysis
84+
85+
| Approach | Time Complexity | Space Complexity |
86+
|----------------|------------------|------------------|
87+
| Brute Force | O(N) | O(N) |
88+
| Optimized | O(1) | O(N) |
89+
| Best Approach | O(1) | O(N) |
90+
91+
---
92+
93+
## 📉 Optimization Ideas
94+
95+
- Use a custom DLL instead of built-in collections for better control over node management.
96+
- Avoid unnecessary key re-insertion in hashmap.
97+
98+
---
99+
100+
## 📌 Example Walkthroughs & Dry Runs
101+
102+
Capacity = 2
103+
put(1,1) → cache = {1=1}
104+
put(2,2) → cache = {1=1, 2=2}
105+
get(1) → return 1 → cache = {2=2, 1=1}
106+
put(3,3) → evict 2 → cache = {1=1, 3=3}
107+
get(2) → return -1 (not found)
108+
put(4,4) → evict 1 → cache = {3=3, 4=4}
109+
get(1) → return -1 (not found)
110+
get(3) → return 3
111+
get(4) → return 4
112+
113+
---
114+
115+
116+
## 🔗 Additional Resources
117+
118+
- [Stack Visualization Tool](https://visualgo.net/en/list)
119+
120+
Author: Neha Amin
121+
Date: 19/07/2025
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
class LRUCache {
2+
public:
3+
class node {
4+
public:
5+
int key;
6+
int val;
7+
node *next;
8+
node *prev;
9+
node(int _key, int _val) {
10+
key = _key;
11+
val = _val;
12+
next = nullptr;
13+
prev = nullptr;
14+
}
15+
};
16+
17+
node *head = new node(-1, -1);
18+
node *tail = new node(-1, -1);
19+
20+
int cap;
21+
unordered_map<int, node*> m;
22+
23+
LRUCache(int capacity) {
24+
cap = capacity;
25+
head->next = tail;
26+
tail->prev = head;
27+
}
28+
29+
void addnode(node *newnode) {
30+
node *temp = head->next;
31+
newnode->next = temp;
32+
newnode->prev = head;
33+
head->next = newnode;
34+
temp->prev = newnode;
35+
}
36+
37+
void deletenode(node *delnode) {
38+
node *delprev = delnode->prev;
39+
node *delnext = delnode->next;
40+
delprev->next = delnext;
41+
delnext->prev = delprev;
42+
}
43+
44+
int get(int key_) {
45+
if (m.find(key_) != m.end()) {
46+
node *resnode = m[key_];
47+
int res = resnode->val;
48+
m.erase(key_);
49+
deletenode(resnode);
50+
addnode(resnode);
51+
m[key_] = head->next;
52+
return res;
53+
}
54+
return -1;
55+
}
56+
57+
void put(int key_, int value) {
58+
if (m.find(key_) != m.end()) {
59+
node *existingnode = m[key_];
60+
m.erase(key_);
61+
deletenode(existingnode);
62+
}
63+
if (m.size() == cap) {
64+
m.erase(tail->prev->key);
65+
deletenode(tail->prev);
66+
}
67+
addnode(new node(key_, value));
68+
m[key_] = head->next;
69+
}
70+
};

0 commit comments

Comments
 (0)