146. LRU Cache
MediumView on LeetCode
Problem Overview
An LRU cache needs O(1) lookup and O(1) reordering to track recency.
Advertisement
Intuition
An LRU cache needs O(1) lookup and O(1) reordering to track recency. A hash map gives O(1) lookup. A doubly-linked list gives O(1) insertion and deletion at any position. Combining both: the map stores key -> node, and the list maintains order from most-recent (head) to least-recent (tail).
Algorithm
- 1Create dummy head and tail nodes. Connect them. Map starts empty.
- 2get(key): if not in map, return -1. Otherwise move the node to just after the dummy head and return its value.
- 3put(key, value): if key exists, update value and move to front. If new: insert new node at front.
- 4After inserting a new node: if size > capacity, remove the node just before the dummy tail (LRU) and delete its key from the map.
- 5Helper Remove(node) unlinks a node in O(1). Helper InsertFront(node) inserts after head in O(1).
Example Walkthrough
Input: capacity=2, operations: put(1,1), put(2,2), get(1), put(3,3), get(2)
- 1.put(1,1): list = [1]. put(2,2): list = [2,1] (2 is most recent).
- 2.get(1): move 1 to front -> list = [1,2]. Return 1.
- 3.put(3,3): list full. Evict LRU (tail = 2). list = [3,1].
- 4.get(2): key 2 was evicted -> return -1.
Output: get(1)=1, get(2)=-1
Common Pitfalls
- •Sentinel dummy head and tail nodes eliminate all null checks in Remove/InsertFront.
- •Always remove the old node before inserting the updated one to avoid the map having two entries for the same key.
- •On eviction, remove from BOTH the list and the map or subsequent gets will return stale data.
146.cs
C#
// Approach: Combine a doubly-linked list with a Dictionary<key, node> for O(1) get and put.
// The list maintains LRU order: most-recently used node sits at the head, least-recently at the tail.
// On get: if the key exists, detach the node, move it to the head, and return its value.
// On put: if the key exists, update and move to head; if new, insert a new node at the head.
// If inserting exceeds capacity, remove the node at the tail (least recently used) and its map entry.
// Sentinel head and tail dummy nodes eliminate null-pointer edge cases in all list operations.
// Time: O(1) per get/put. Space: O(capacity) for the list and map combined.
public class LRUCache
{
private int capacity;
private Node head = new Node(0, 0), tail = new Node(0, 0);
private Dictionary<int, Node> map = new Dictionary<int, Node>();
public LRUCache(int capacity)
{
this.capacity = capacity;
head.next = tail;
tail.prev = head;
}
public int Get(int key)
{
if (!map.ContainsKey(key))
return -1;
Node node = map[key];
remove(node);
insert(node);
return node.Value;
}
public void Put(int key, int value)
{
if (map.ContainsKey(key))
remove(map[key]);
if (map.Count == capacity)
remove(tail.prev);
insert(new Node(key, value));
}
private void remove(Node node)
{
map.Remove(node.Key);
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void insert(Node node)
{
map.Add(node.Key, node);
node.next = head.next;
node.next.prev = node;
head.next = node;
node.prev = head;
}
}
class Node
{
public int Key;
public int Value;
public Node prev, next;
public Node(int key, int value)
{
this.Key = key;
this.Value = value;
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.Get(key);
* obj.Put(key,value);
*/Advertisement
Was this solution helpful?
Related Problems
- 19. Remove Nth Node From End of List(Medium)
- 25. Reverse Nodes in k-Group(Hard)
- 30. Substring with Concatenation of All Words(Hard)
- 36. Valid Sudoku(Medium)
- 61. Rotate List(Medium)
- 73. Set Matrix Zeroes(Medium)