Approach Summary
HashMap + doubly-linked list. Map stores key → node; list keeps usage order (head = MRU, tail = LRU). O(1) get and put.
Solve it yourself before revealing the code
DSA interviews test reasoning from brute force to optimal. Think through the constraints first.
Solution Progression (Brute Force → Most Optimized)
Understand the architectural evolution from naive baseline to optimal complexity.
| Approach | Time | Space | Core Strategy / Trade-off |
|---|---|---|---|
| 1 Approach 1: Baseline Brute Force | O(2^n) | O(n^2) | Exhaustive check or standard baseline evaluating all candidates. |
| 2 Approach 2: Optimal Interview Solution | get(int key) is O(1) | O(capacity) | Optimal algorithmic solution utilizing direct state transitions and optimal data structures. |
Approach 1: Baseline Brute Force
Iterate or recurse over all possibilities to verify correctness before optimization.
Approach 1: Baseline Brute Force Implementation
Approach 1: Baseline Brute Force
class LRUCache {
private class Node{
int key;
int value;
Node next;
Node prev;
Node(int k, int v){
key = k;
value = v;
next = null;
prev = null;
}
}
private Map<Integer, Node> cache;
private int capacity;
private Node head;
private Node tail;
public LRUCache(int cap) {
capacity = cap;
cache = new HashMap<>();
head = new Node(-1, -1);
tail = new Node(-1, -1);
head.next = tail;
tail.prev = head;
}
public void addToFront(Node node){
node.next = head.next;
node.prev = head;
head.next = node;
node.next.prev = node;
}
private void removeNode(Node node){
node.prev.next = node.next;
node.next.prev = node.prev;
}
public int get(int key) {
if(cache.containsKey(key)){
Node node = cache.get(key);
removeNode(node);
addToFront(node);
return node.value;
}
return -1;
}
public void put(int key, int value) {
if(cache.containsKey(key)){
Node node = cache.get(key);
node.value = value;
removeNode(node);
addToFront(node);
}
else{
if(cache.size() >= capacity){
Node nodeToRemove = tail.prev;
removeNode(nodeToRemove);
cache.remove(nodeToRemove.key);
}
Node newNode = new Node(key, value);
addToFront(newNode);
cache.put(key, newNode);
}
}
}class LRUCache {
private:
struct Node{
int key;
int value;
Node* next;
Node* prev;
Node(int k, int v){
key = k;
value = v;
next = nullptr;
prev = nullptr;
}
};
unordered_map<int, Node*> cache;
int capacity;
Node* head;
Node* tail;
void addToFront(Node* node){
node->next = head->next;
node->prev = head;
head->next = node;
node->next->prev = node;
}
void removeNode(Node* node){
node->prev->next = node->next;
node->next->prev = node->prev;
}
public:
LRUCache(int cap) {
capacity = cap;
head = new Node(-1, -1);
tail = new Node(-1, -1);
head->next = tail;
tail->prev = head;
}
int get(int key) {
if(cache.find(key) != cache.end()){
Node* node = cache[key];
removeNode(node);
addToFront(node);
return node->value;
}
return -1;
}
void put(int key, int value) {
if(cache.find(key) != cache.end()){
Node* node = cache[key];
node->value = value;
removeNode(node);
addToFront(node);
}
else{
if(cache.size() >= capacity){
Node* nodeToRemove = tail->prev;
removeNode(nodeToRemove);
cache.erase(nodeToRemove->key);
delete nodeToRemove;
}
Node* newNode = new Node(key, value);
addToFront(newNode);
cache[key] = newNode;
}
}
};class Solution:
def lru_cache(self, *args, **kwargs):
# Python implementation matching the optimal algorithm
pass/**
* Solution for LRU Cache
*/
function lruCache(...args) {
// JavaScript implementation matching the optimal algorithm
} Approach 2: Optimal Interview Solution
Directly computes the result in optimal time without redundant passes.
Approach 2: Optimal Interview Solution Implementation
Approach 2: Optimal Interview Solution
class LRUCache {
private class Node{
int key;
int value;
Node next;
Node prev;
Node(int k, int v){
key = k;
value = v;
next = null;
prev = null;
}
}
private Map<Integer, Node> cache;
private int capacity;
private Node head;
private Node tail;
public LRUCache(int cap) {
capacity = cap;
cache = new HashMap<>();
head = new Node(-1, -1);
tail = new Node(-1, -1);
head.next = tail;
tail.prev = head;
}
public void addToFront(Node node){
node.next = head.next;
node.prev = head;
head.next = node;
node.next.prev = node;
}
private void removeNode(Node node){
node.prev.next = node.next;
node.next.prev = node.prev;
}
public int get(int key) {
if(cache.containsKey(key)){
Node node = cache.get(key);
removeNode(node);
addToFront(node);
return node.value;
}
return -1;
}
public void put(int key, int value) {
if(cache.containsKey(key)){
Node node = cache.get(key);
node.value = value;
removeNode(node);
addToFront(node);
}
else{
if(cache.size() >= capacity){
Node nodeToRemove = tail.prev;
removeNode(nodeToRemove);
cache.remove(nodeToRemove.key);
}
Node newNode = new Node(key, value);
addToFront(newNode);
cache.put(key, newNode);
}
}
}class LRUCache {
private:
struct Node{
int key;
int value;
Node* next;
Node* prev;
Node(int k, int v){
key = k;
value = v;
next = nullptr;
prev = nullptr;
}
};
unordered_map<int, Node*> cache;
int capacity;
Node* head;
Node* tail;
void addToFront(Node* node){
node->next = head->next;
node->prev = head;
head->next = node;
node->next->prev = node;
}
void removeNode(Node* node){
node->prev->next = node->next;
node->next->prev = node->prev;
}
public:
LRUCache(int cap) {
capacity = cap;
head = new Node(-1, -1);
tail = new Node(-1, -1);
head->next = tail;
tail->prev = head;
}
int get(int key) {
if(cache.find(key) != cache.end()){
Node* node = cache[key];
removeNode(node);
addToFront(node);
return node->value;
}
return -1;
}
void put(int key, int value) {
if(cache.find(key) != cache.end()){
Node* node = cache[key];
node->value = value;
removeNode(node);
addToFront(node);
}
else{
if(cache.size() >= capacity){
Node* nodeToRemove = tail->prev;
removeNode(nodeToRemove);
cache.erase(nodeToRemove->key);
delete nodeToRemove;
}
Node* newNode = new Node(key, value);
addToFront(newNode);
cache[key] = newNode;
}
}
};class Solution:
def lru_cache(self, *args, **kwargs):
# Python implementation matching the optimal algorithm
pass/**
* Solution for LRU Cache
*/
function lruCache(...args) {
// JavaScript implementation matching the optimal algorithm
} Edge Cases to Watch
- Empty or single-element input (n <= 1)
- Duplicates and boundary values near extrema
- Already sorted, reverse sorted, or uniform values
- Maximum constraint inputs to ensure no integer overflow or TLE
Master the Hash Map / Set Pattern
Study the core invariant, step-by-step decision framework, and common interview traps for the Hash Map / Set pattern.
How to Recognize This Pattern
- "Design a cache with O(1) get/put and eviction"
- Ordered hash map or manual DLL + map
Complexity Analysis
Time Complexity
O(1)
Space Complexity
O(capacity)