LRU Cache
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.
Examples
Constraints
1 <= capacity <= 30000 <= key <= 10^40 <= value <= 10^5
Hash Map + Doubly Linked List
Approach
Use a Hash Map to get nodes in O(1) time. Use a Doubly Linked List to maintain order of usage. The most recently used node is kept at the head (right next to dummy head), and the least recently used node is kept at the tail (right before dummy tail). When a node is accessed or added, move/add it to the head. When capacity is exceeded, remove the node before the dummy tail from both the list and the hash map.
Complexity Analysis
This design requires an understanding of how to implement and manipulate a doubly linked list.
class Node { int key, val; Node prev, next; public Node(int key, int val) { this.key = key; this.val = val; }}
class LRUCache { private int capacity; private Map<Integer, Node> cache; private Node head, tail; // Dummy nodes
public LRUCache(int capacity) { this.capacity = capacity; this.cache = new HashMap<>(); this.head = new Node(-1, -1); this.tail = new Node(-1, -1); head.next = tail; tail.prev = head; } // Helper to insert right after head private void insert(Node node) { node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; } // Helper to remove a node private void remove(Node node) { node.prev.next = node.next; node.next.prev = node.prev; } public int get(int key) { if (!cache.containsKey(key)) return -1; Node node = cache.get(key); remove(node); insert(node); // Move to most recently used return node.val; } public void put(int key, int value) { if (cache.containsKey(key)) { Node node = cache.get(key); remove(node); } Node newNode = new Node(key, value); cache.put(key, newNode); insert(newNode); if (cache.size() > capacity) { // Remove LRU node from list and map Node lru = tail.prev; remove(lru); cache.remove(lru.key); } }}