The main point of this problem is to guarantee each operation cost O(1) time, This is the main requirement of cache.
Since we need to remove a record from the cache and keep all records in order, this invloves searching, deletion and insertion. The best data structure to do this is the
one which is a combination of HashMap and doubly-linked list. So, I use a HashMap<Integer, Node>, where Node is class defined by myself.
public class LRUCache {
public LRUCache(int capacity) {
lruMap = new HashMap<Integer, Node>();
this.capacity = capacity;
sz = 0;
}
public int get(int key) {
int result = -1;
if(lruMap.containsKey(key)){
moveToHead(lruMap.get(key));
lruMap.put(key, first);
result = first.value;
}
return result;
}
public void set(int key, int value) {
if(lruMap.containsKey(key)){
Node temp = lruMap.get(key);
temp.value = (temp.value == value )? temp.value : value;
moveToHead(temp);
lruMap.put(key, first);
}
else if(sz < capacity){
Node node = new Node();
node.key = key;
node.value = value;
if(sz == 0){
first = node;
last = node;
}
else{
first.prev = node;
node.next = first;
first = node;
}
lruMap.put(key, node);
++sz;
}
else{
Node node = new Node();
node.key = key;
node.value = value;
int lastKey = last.key;
if(sz == 1){
last = null;
first = null;
first = node;
last = node;
}
else{
last = last.prev;
last.next = null;
first.prev = node;
node.next = first;
first = node;
}
lruMap.remove(lastKey);
lruMap.put(key, node);
}
}
public void moveToHead(Node node){
if( node.prev == null) //the node is already the head. Do nothing
return;
else{
node.prev.next = node.next;
if(node.next != null)
node.next.prev = node.prev;
else
last = node.prev;
first.prev = node;
node.prev = null;
node.next = first;
first = node;
}
}
private HashMap<Integer, Node> lruMap;
private int capacity;
private Node first;
private Node last;
private int sz;
}
class Node{
int key; //we need use it to remove a node in the HashMap;
int value;
Node prev;
Node next;
//using the default constructor is enough
}