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 exists 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 a new item.
For this problem, we need to build our own double linked list. We also need a HashMap to map the key and the node pair. To use LinkedHashMap might be a bit unfair here. Why using double linked list instead of single linked list? The reason is that after each operation of certain element, we have to move the element to the tail of the list. We also need to change the pointer of its previous element. So a previous pointer would be necessary. Besides, we add a head and a tail to help us in the implementation.
class Node {
int key;
int val;
Node next;
Node prev;
public Node(int x, int y) {
key = x;
val = y;
next = null;
prev = null;
}
}
public class LRUCache {
int volume;
int head;
int tail;
HashMap<Integer, Node> cacheMap;
public LRUCache(int capacity) {
volume = capacity;
cacheMap = new HashMap<Integer, Node>();
}
public int get(int key) {
Node cur = cacheMap.get(key);
if (cur != null) {
// if key is in the tail, no need to move
if (key == tail) {
return cacheMap.get(tail).val;
}
// we need to update the head
if (key == head) {
head = cacheMap.get(head).next.key;
}
// remove that node
else {
cur.prev.next = cur.next;
}
cur.next.prev = cur.prev;
// put cur in the tail
Node tailNode = cacheMap.get(tail);
tailNode.next = cur;
cur.prev = tailNode;
tail = cur.key;
return cur.val;
}
return -1;
}
public void set(int key, int value) {
Node cur = cacheMap.get(key);
if (cur != null) {
cur.val = value;
this.get(key);
}
else {
Node insertNode = new Node(key, value);
cacheMap.put(key, insertNode);
// if the map could fit a new node, we put it in and updata the tail
if (cacheMap.size() == 1) {
head = key;
tail = key;
return;
}
Node tailNode = cacheMap.get(tail);
tailNode.next = insertNode;
insertNode.prev = tailNode;
tail = key;
// update tail
// check if we reach the volume
if (cacheMap.size() > volume) {
int newHead = cacheMap.get(head).next.key;
cacheMap.get(newHead).prev = null;
cacheMap.remove(head);
head = newHead;
}
}
}
}