This code implements an LRUCache (Least Recently Used Cache) using a combination of a Map
and a Queue
. The LRUCache is designed to store key-value pairs with a fixed capacity. When the capacity is exceeded, the least recently used (LRU) key-value pair is evicted from the cache.
-
The
LRUCache
class contains the following data members:k
: An integer representing the capacity of the cache.mp
: AMap
that stores key-value pairs. The key is an integer representing the key, and the value is a list of two integers: the actual value and the frequency of usage.q
: AQueue
that keeps track of the order of keys based on their usage. The front of the queue represents the least recently used key, and the rear represents the most recently used key.
-
The
LRUCache
constructor initializes the capacityk
. -
The
get
method is used to retrieve the value associated with a given key from the cache. If the key exists, its frequency of usage is incremented, and the key is moved to the rear of the queue (indicating it was recently used). The value associated with the key is returned; otherwise, -1 is returned if the key does not exist in the cache. -
The
put
method is used to add a new key-value pair to the cache. If the key already exists, its value is updated, the frequency of usage is incremented, and the key is moved to the rear of the queue (indicating it was recently used). If the key is not present in the cache, a new key-value pair is created, added to theMap
, and the key is added to the rear of the queue. If the cache capacity is exceeded after inserting the new key-value pair, the least recently used key-value pair (front of the queue) is evicted from the cache.
- The space complexity of the LRUCache is O(k), where k is the capacity of the cache. This is because the
Map
(mp
) andQueue
(q
) store a maximum of k elements.
- The time complexity of the
get
method is O(1) as it involves simple lookups and queue operations. - The time complexity of the
put
method is also O(1) on average. The worst-case time complexity occurs when the cache reaches its capacity and the least recently used key-value pair needs to be evicted. In this case, a while loop iterates through theQueue
to find and remove the LRU element. However, since each element can be evicted at most once, the overall amortized time complexity remains constant for theput
method.
class LRUCache {
late int k;
final Map<int, List<int>> mp = Map();
final Queue<int> q = Queue<int>();
LRUCache(int capacity) {
k = capacity;
}
int get(int key) {
if (mp.containsKey(key)) {
mp[key]![1]++;
q.add(key);
return mp[key]![0];
}
return -1;
}
void put(int key, int value) {
if (mp.containsKey(key)) {
mp[key]![0] = value;
mp[key]![1]++;
q.add(key);
} else {
mp[key] = [value, 1];
q.add(key);
}
// Check if capacity is exceeded.
if (mp.length > k) {
while (q.isNotEmpty) {
int ky = q.removeFirst();
// Decrement the frequency of ky.
mp[ky]![1]--;
// If no more key present in the queue, that means it is the least recently used.
if (mp[ky]![1] == 0) {
mp.remove(ky);
break;
}
}
}
}
}
import 'dart:collection';
class LRUCache {
final Queue<int> leastUsedCache = Queue<int>();
final List<int> usages = List<int>.filled(10001, 0);
final List<int> keyValuePair = List<int>.filled(10001, -1);
int size = 0;
int cap = 0;
LRUCache(int capacity) {
cap = capacity;
}
int get(int key) {
if (keyValuePair[key] != -1) {
leastUsedCache.add(key);
usages[key]++;
}
return keyValuePair[key];
}
void put(int key, int value) {
if (size < cap || keyValuePair[key] != -1) {
if (keyValuePair[key] == -1) size++;
leastUsedCache.add(key);
usages[key]++;
keyValuePair[key] = value;
return;
}
while (usages[leastUsedCache.first] != 1) {
usages[leastUsedCache.first]--;
leastUsedCache.removeFirst();
}
keyValuePair[leastUsedCache.first] = -1;
usages[leastUsedCache.first]--;
leastUsedCache.removeFirst();
leastUsedCache.add(key);
usages[key]++;
keyValuePair[key] = value;
}
}
Intuition and Approach:
This code implements an LRUCache (Least Recently Used Cache) using a combination of a Map
and a linked list of Node
objects. The LRUCache is designed to store key-value pairs with a fixed capacity. When the capacity is exceeded, the least recently used (LRU) key-value pair is evicted from the cache.
-
The
Node
class represents individual nodes in a doubly linked list, each holding akey
and aval
(value) representing the key-value pair. -
The
LRUCache
class contains the following data members:cap
: An integer representing the capacity of the cache.mp
: AMap
that stores key-value pairs, where the key is an integer representing the key, and the value is aNode
representing the key-value pair stored in the linked list.head
: The dummyNode
that acts as the head of the doubly linked list.tail
: The dummyNode
that acts as the tail of the doubly linked list.
-
The
LRUCache
constructor initializes the capacitycap
, and sets up the dummy head and tail nodes of the doubly linked list. -
The
addNode
method adds a newNode
to the front (after the dummy head) of the doubly linked list. -
The
deleteNode
method removes a givenNode
from the doubly linked list. -
The
get
method is used to retrieve the value associated with a given key from the cache. If the key exists, its correspondingNode
is moved to the front of the doubly linked list (indicating it was recently used), and its value is returned. If the key does not exist in the cache, -1 is returned. -
The
put
method is used to add a new key-value pair to the cache. If the key already exists, its correspondingNode
is removed from the cache, and the new key-value pair is added as a newNode
to the front of the doubly linked list. If the cache capacity is exceeded after inserting the new key-value pair, the least recently used key-value pair (thetail.prev
) is evicted from the cache.
- The space complexity of the LRUCache is O(cap), where
cap
is the capacity of the cache. This is because theMap
(mp
) stores at mostcap
key-value pairs, and the doubly linked list stores at mostcap
Node
objects.
- The time complexity of the
get
method is O(1) as it involves simple map lookups and linked list operations. - The time complexity of the
put
method is O(1) on average. The worst-case time complexity occurs when the cache reaches its capacity, and the least recently used key-value pair needs to be evicted. In this case, themp.remove
anddeleteNode
operations take constant time, and thus, the overall amortized time complexity remains constant for theput
method.
class Node {
late final int key;
late final int val;
Node? prev = null;
Node? next = null;
Node(this.key, this.val);
}
class LRUCache {
late int cap;
final Map<int, Node> mp = {};
final Node head = Node(-1, -1);
final Node tail = Node(-1, -1);
LRUCache(int capacity) {
cap = capacity;
head.next = tail;
tail.prev = head;
}
void addNode(Node newNode) {
Node? temp = head.next;
newNode.next = temp;
newNode.prev = head;
head.next = newNode;
temp?.prev = newNode;
}
void deleteNode(Node delNode) {
Node? delPrev = delNode.prev;
Node? delNext = delNode.next;
delPrev?.next = delNext;
delNext?.prev = delPrev;
}
int get(int key) {
if (mp.containsKey(key)) {
Node resNode = mp[key]!;
int res = resNode.val;
deleteNode(resNode);
addNode(resNode);
mp[key] = head.next!;
return res;
}
return -1;
}
void put(int key, int value) {
if (mp.containsKey(key)) {
Node existingNode = mp[key]!;
mp.remove(key);
deleteNode(existingNode);
}
if (mp.length == cap) {
mp.remove(tail.prev!.key);
deleteNode(tail.prev!);
}
addNode(Node(key, value));
mp[key] = head.next!;
}
}