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.
作为一个题目看了好久才看懂的菜鸟,有必要在这里解释一下这道题的意思。
在set的时候,如果key已经存在,那么将原来的值替换为value,如果不存在,并且已经到达容量了,那么需要删除最久没有访问的一个元素,然后再加入新的元素。
看了key和value,第一想法是map,然后再加上附加要求,那么感觉使用linkedHashMap比较合适,每次查询或者更新元素的时候,将map中原来的元素删除,再加入新的元素;如果容量不够,删除第一个元素就行。
写完程序,提交,结果提示找不到linkedHashMap这个类。限制了对LinkedHashMap的使用。
于是又了第二个想法,用map存储key和value的关系。用linkedList存取key的顺序,当对当前key有操作时,将当前key在list中的位置移到最后。如果需要删除元素, 删除当前元素的第一个值即可。于是有了以下程序:
class LRUCache {
private int capacity;
private Map<Integer, Integer> map;
private List<Integer> list;
public LRUCache2(int capacity) {
this.capacity=capacity;
map=new HashMap<Integer, Integer>();
list=new LinkedList<Integer>();
}
public int get(int key) {
if(map.containsKey(key)){
Iterator<Integer> iterator=list.iterator();
while(iterator.hasNext()){
if(iterator.next()==key){
iterator.remove();
break;
}
}
list.add(key);
return map.get(key);
}
return -1;
}
public void set(int key, int value) {
if(map.size()<capacity||map.containsKey(key))
map.put(key, value);
else {
map.put(key, value);
}
}
}
其实在提交之前就知道这个程序的弊端,因为采用了linkedList,没法精准的得到当前key在list中的位置,所以需要从头开始找,时间复杂度比较高,提交代码以后,果然超时。
为了解决以上问题,看了下讨论区的内容,然后发现,一个思路类似的方法,自定义每个节点,节点保存key和value的值,使用map存取key和node的对应关系,很好的解决了上面程序存在的问题,程序如下:
private class Node{
int key;
int value;
Node next;
Node pre;
public Node(int key,int val){
this.key=key;
value=val;
pre=null;
next=null;
}
}
// Doubly link list of type Node.
private class DoublyLL{
Node head;
Node last;
int capacity;
int count;
public DoublyLL(int c){
capacity=c;
count=0;
head=new Node(0, 0);
last=new Node(0, 0);
head.next=last;
last.pre=head;
}
}
HashMap<Integer, Node> map;// The map will store a reference to the Node in
// DLL corresponding to each key.
DoublyLL dll;
public LRUCache(int capacity){
dll=new DoublyLL(capacity);
map=new HashMap<Integer,Node>();
}
public int get(int key){
if(map.containsKey(key)){
Node n=map.get(key);
if(n.pre!=dll.head){
detach(n);
attach(n);
}
return n.value;
}else {
return -1;
}
}
public void set(int key,int value){
Node n;
if(!map.containsKey(key)){
n=new Node(key, value);
if(dll.capacity!=dll.count)
dll.count++;
else {
map.remove(dll.last.pre.key);
detach(dll.last.pre);
}
attach(n);
map.put(key, n);
}else {
n=map.get(key);
n.value=value;
detach(n);
attach(n);
}
}
private void attach(Node node) {
node.pre = dll.head;
node.next = dll.head.next;
dll.head.next.pre = node;
dll.head.next = node;
}
private void detach(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
}