Today's problem asks to design a data structure for a Least Recently Used cache (LRU cache).
For this problem, an LRU cache stores a set of values up to a certain size. If a value is added after the cache is full, a value must be removed. The way to determine which value to remove is based on the LRU, or the value that was least recently used.
A data structure must be designed that provides get and set methods that work in constant time. To accomplish this task, we use a doubly-linked list, and a hash map that stores a pointer to an element in the doubly-linked list. The linked-list represents the order of key's use, with the front being the most recently used key, and the end the LRU. The Standard Template Library supports a doubly-linked list data structure, as well as a hash map. We also use an integer to store the size of the data structures.
list<pair<int, int>> dll;
unordered_map<int, list<pair<int, int>>::iterator> hashmap;
int cap;
Above we have a doubly linked list dll, and a hashmap that, for a given key, points to the key's placement in the linked list.
We implement the data structure declaration which designates its size
LRUCache(int cap) :cap(cap) {}
For the get method, we first check the hashmap. If the key isn't in the hashmap, then return -1 as defined by the problem. Otherwise return a pointer to the key's position in the linked list. In accordance with LRU, we need to update the key's position in the linked list to the front, as it's the most recently used.
int get(int key) {
// Check if key exists, if not return -1
if (hashmap.find(key) == hashmap.end() )
return -1; // Key not found, return -1
// get the pointer to key in the linked list, dereference for key/val pair list<pair<int,int> >::iterator keyptr = hashmap[key];
pair<int,int> keypair = *keyptr;
// Delete key in linked list, add it to front of list
dll.erase(keyptr);
dll.push_front(keypair);
// get new pointer to list start, set to key in hashmap
list<pair<int, int>>::iterator newFront = dll.begin();
hashmap[key] = newFront;
// return the value return keypair.second;
}
Finally the put method. First we check if the key already exists in the cache, and if it does, bring it to the front, similar to what occurs in the get method:
void put(int key, int value) {
if (hashmap.find(key) != hashmap.end() ) {
list<pair<int,int> >::iterator itr = hashmap[key];
pair<int,int> pair = *itr;
dll.erase(itr);
dll.push_front(pair);
hashmap[key] = dll.begin();
return;
}
The we check if the cache is full, and if so, delete the LRU by removing the list's last element
if (dll.size() == cap) {
pair<int,int> lastElement = dll.back();
hashmap.erase(lastElement.first);
dll.pop_back();
}
Finally, we add the key/value pair to the front of the cache
dll.push_front(make_pair(key, value) );
hashmap[key] = dll.begin();
}
Very useful to learn of the STL containers list and forwar_list, doubly and singly linked lists, respectively
Thanks to user anwendeng for help clearing up the get method
Comments
Post a Comment