Least Recently Used (LRU) is a cache eviction policy that keeps track of how recently data is used. When the cache has no space for new data, it removes the item that has not been accessed for the longest time and stores the new item instead.
Frequently accessed data is given higher priority in the cache because it has a strong chance of being needed again soon.
Data that stays unused for a long time is assigned lower priority and is removed first when the cache becomes full.
Example: If a cache can store 3 pages and the access sequence is A -> B -> C -> A -> D, page B is evicted because it is the least recently used (LRU) item.
These operations help manage data efficiently in the cache by storing, accessing, updating, and removing items based on their recent usage.
LRU Cache(capacity c): Initializes the LRU cache with a fixed capacity c.
get(key): Returns the value associated with the given key if it exists in the cache; otherwise, returns -1. The accessed item is marked as the most recently used.
put(key, value): Inserts a new key-value pair or updates the value of an existing key. If the cache exceeds its capacity, the least recently used item is removed.
Working of LRU Cache
Let's suppose we have an LRU cache of capacity 3, and we would like to perform the following operations:
put (key=1, value=A) into the cache
put (key=2, value=B) into the cache
put (key=3, value=C) into the cache
get (key=2) from the cache
get (key=4) from the cache
put (key=4, value=D) into the cache
put (key=3, value=E) into the cache
get (key=4) from the cache
put (key=1, value=A) into the cache
The above operations are performed one after another as shown in the image below:
put(1, A): The cache is empty, so {1:A} is added directly and becomes the most recently used item.
put(2, B): The cache still has space, so {2:B} is added. It now becomes the most recently used item, while {1:A} moves to lower priority.
put(3, C): The cache reaches full capacity after adding {3:C}. Priority order: {3:C} → {2:B} → {1:A}
get(2): Returns value B and marks key 2 as the most recently used item. New priority order: {2:B} → {3:C} → {1:A}
get(4): Key 4 is not present in the cache, so the operation returns -1.
put(4, D): The cache is full, so the least recently used item {1:A} is removed. {4:D} is added as the most recently used item. New priority order: {4:D} → {2:B} → {3:C}
put(3, E): Key 3 already exists, so its value is updated from C to E. It also becomes the most recently used item. New priority order: {3:E} → {4:D} → {2:B}
get(4): Returns value D and updates key 4 as the most recently used item. New priority order: {4:D} → {3:E} → {2:B}
put(1, A): The cache is full again, so the least recently used item {2:B} is removed. {1:A} is inserted as the most recently used item. Final priority order: {1:A} → {4:D} → {3:E}
Output: [1, -1] Reason: Key 2 was the least recently used item, so it was removed when key 3 was inserted.
LRU Cache Implementation Methods
Array-Based Implementation: Stores key-value pairs with timestamps in an array. Since searching and updating require scanning the array, both get() and put() operations take O(n) time.
Using Hashing and Heap: Hashing improves lookup speed, while a heap helps identify the least recently used item. However, some operations still require O(log n) time, so O(1) performance is not achieved.
HashMap + Doubly Linked List (Optimal): Uses a hash map to locate cache entries in O(1) time and a doubly linked list to maintain usage order. Both get() and put() operations run in O(1) time, making this the standard and most efficient implementation of an LRU cache.
Efficient Solution - Using Doubly Linked List and Hashing
The LRU (Least Recently Used) cache is commonly implemented using a combination of a hash map and a doubly linked list to perform insertion, deletion, and access operations efficiently.
New key-value pairs are inserted at the head of the doubly linked list, making them the most recently used items. If a key is accessed or updated, its node is moved to the head of the list.
The doubly linked list maintains priority based on usage. Nodes near the head are recently used, while nodes near the tail are least recently used.
When the cache reaches maximum capacity, the node at the tail of the list is removed because it represents the least recently used item. The corresponding entry is also removed from the hash map to free space for new data.