The most important thing to solve this problem is to choose the right data structure.
#include <iostream>
#include <unordered_map>
using namespace std;
/*
Design and implement a data structure for Least Recently Used(LRU) cache. It should support
the following operations: get and set.
get(key) -- get the value(will always be positive) of the key if the key exisits in the cache,
otherwise, return -1.
set(key, value) - set or insert the value if the key is not already present. When the cache
reached its capacity, it should invalidate the least recently used item before
inserting the new item.
*/
/*
1: get operation, once we get the value and result not equals to -1, we need to update
the value to be the most recent used one.
2: set operation need to pay attentions to the capacity. Once it is full and set a new
value need to delete the most recent visited ones.
Inorder to complete these operations effciently, we need to consider the data structure.
Represention of cache:
1: Array, get takes O(N) time (key is not index), update takes O(N) time, set takes O(N).
2: LinkList, get takes O(N) time, update takes O(1) time, set takes O(N) time.
3: Stack, get takes O(N), update takes O(N), set takes O(N). (complicate to implement)
4: Tree, get takes O(lgN), update takes O(lgN), set takes O(lgN). (complicate to implement)
5: HashMap for get operation, take O(1), LinkList for Update and set. O(N) time.
Seems the 5th choice beats others.
*/
class LRUCache {
private:
struct ListNode {
int key;
int value;
ListNode* next;
ListNode(int x, int y) : key(x), value(y), next(NULL) {}
};
unordered_map<int, int> keyToValue;
int cacheSize;
ListNode* head;
private:
ListNode* UpdateToMostRecent(ListNode* head, int value) {
ListNode* dummy = new ListNode(0, 0);
dummy->next = head;
ListNode* prev = dummy;
ListNode* current = head;
while(current) {
if(current->key == value) {
prev->next = current->next;
current->next = dummy->next;
dummy->next = current;
} else {
prev = current;
current = current->next;
}
}
ListNode* newHead = dummy->next;
delete dummy;
return newHead;
}
int getCapacity(ListNode* head) {
int size = 0;
ListNode* tmp = head;
while(tmp) {
size++;
tmp = tmp->next;
}
return size;
}
public:
LRUCache(int capacity) {
this->cacheSize = capacity;
this->head = NULL;
}
int get(int key) {
auto iter = keyToValue.find(key);
if(iter != keyToValue.end()) {
head = UpdateToMostRecent(head, iter->second);
return iter->second;
} else {
return -1;
}
}
void set(int key, int value) {
auto iter = keyToValue.find(key);
if(iter == keyToValue.end()) {
keyToValue.insert({key, value});
if(getCapacity(head) == cacheSize) {
ListNode* tmp = head;
while(tmp->next) {
tmp = tmp->next;
}
tmp->next = new ListNode(key, value);
ListNode* newHead = head->next;
auto it = keyToValue.find(head->key);
keyToValue.erase(it);
delete head;
head = newHead;
} else {
if(!head) head = new ListNode(key, value);
else {
ListNode* current = head;
while(current->next) current = current->next;
current->next = new ListNode(key, value);
}
}
} else {
ListNode* tmp = head;
while(tmp) {
if(tmp->key == key) {
tmp->value = value;
}
tmp = tmp->next;
}
}
}
};
int main(void) {
LRUCache cache(3);
cache.set(1, 11);
cache.set(2, 12);
cache.set(3, 13);
cout << cache.get(1) << endl;
cache.set(4, 14);
cout << cache.get(1) << endl;
}
A much much better version : using double linked list, maintain a head and a tail.
struct Node {
int key;
int value;
Node* prev;
Node* next;
};
class LRUCacheII{
private int capacity;
private map<int, Node> data;
private Node head;
private Node end;
public LRUCacheII(int capacity) {
this.capacity = capacity;
this.data = new map<>();
}
// append node as the head
void add(Node node) {
node.next = NULL;
node.prev = NULL;
if(NULL == this.head) {
this.head = node;
this.end = node;
return;
}
this.head.prev = node;
node.next = this.head;
this.head = node;
}
// remove a node from the double linked list.
private void remove(Node node) {
if(this.head == NULL || node == NULL) return;
if(this.head == this.end && this.head == node) {
this.head = NULL;
this.end = NULL;
return;
}
if(node = this.head)
this.head.next.prev = NULL;
this.head = this.head.next;
return;
if(node == this.end) {
this.end.prev.next = NULL;
this.end = this.end.prev;
return;
}
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveFirst(Node node) {
this.remove(node);
this.add(node);
}
private void removeLast() {
this.remove(this.end)
}
public int get(int key) {
if(this.data.find(key)) {
Node node = this.data.get(key);
this.moveFirst(node);
return node.data;
}
return -1;
}
void set(int key, int val) {
if(this.data.find(key)) {
Node node = this.data.get(key);
this.moveFirst(node);
node.data = val;
return;
}
if(this.data.size() >= this.capacity) {
int id = this.end.key();
this.removeLast();
this.data.erase(id);
}
Node node = new Node();
node.key = key;
node.data = val;
this.add(node);
this.data.insert(key, node);
}
}