题目原文:
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.
题目意思:
设计并实现Least Recently Used (LRU) cache,需要支持get和set操作。
get(key) - 如果cache中存在key,则返回key所对应的值value,不存在则返回-1。
set(key,value) - 如果cache已经存在key,则更新,如果不存在,则插入。如果在插入之前,该cache已经满了,则应该先丢弃最近最少使用的item,然后再插入新item。
在OS中,已经学习了LRU原则,每访问一个项(无论是get还是set),则将该项置为最新使用的项。如果插入之前存在key,则将最远使用的项丢弃,然后将该项插入到最近使用的项中。
在知道了LRU原理后,我们就应该考虑实现该算法的时间复杂度的问题。
最先想到的应该是使用HashMap与ArrayList,每set一次,首先判断cache是否已满,是则先丢掉ArrayList中的第0项,然后判断ArrayList是否已经存在该项,存在则先remove,然后add。
将该想法实现后,发现Time Limit Exceeded,然后想了想,问题应该处在ArrayList的remove上,该操作耗费的时间是O(n),故应该改进remove操作。
双向链表的删除操作以及插入操作所耗费的时间都是O(1),故使用双向链表进行实现。
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.
题目意思:
设计并实现Least Recently Used (LRU) cache,需要支持get和set操作。
get(key) - 如果cache中存在key,则返回key所对应的值value,不存在则返回-1。
set(key,value) - 如果cache已经存在key,则更新,如果不存在,则插入。如果在插入之前,该cache已经满了,则应该先丢弃最近最少使用的item,然后再插入新item。
在OS中,已经学习了LRU原则,每访问一个项(无论是get还是set),则将该项置为最新使用的项。如果插入之前存在key,则将最远使用的项丢弃,然后将该项插入到最近使用的项中。
在知道了LRU原理后,我们就应该考虑实现该算法的时间复杂度的问题。
最先想到的应该是使用HashMap与ArrayList,每set一次,首先判断cache是否已满,是则先丢掉ArrayList中的第0项,然后判断ArrayList是否已经存在该项,存在则先remove,然后add。
将该想法实现后,发现Time Limit Exceeded,然后想了想,问题应该处在ArrayList的remove上,该操作耗费的时间是O(n),故应该改进remove操作。
双向链表的删除操作以及插入操作所耗费的时间都是O(1),故使用双向链表进行实现。
建立一个类ListNode,结构如下图所示包含next和pre元素,表示该结点下一个结点与前一个结点,结点结构如下图所示:
用head和tail表示链表的头和尾,其中head.next指向最近使用的结点,tail.pre指向最近最少使用的结点。
对链表和HashMap的操作演示如下图所示:
java实现代码如下(仅供参考,对代码如有疑问,请留言):
package xtu.cie.ldj;
import java.util.HashMap;
public class LRUCache {
private int capacity;
private int currentSize;
private ListNode head; // Recently Used
private ListNode tail;
private HashMap<Integer, ListNode> keyNodeMap;
class ListNode{
int key;
int value;
ListNode pre,next;
ListNode(int k,int v){
key = k; value = v;
pre = null; next = null;
}
}
public LRUCache(int capacity) {
if (capacity > 0) {
this.capacity = capacity;
this.currentSize = 0;
this.keyNodeMap = new HashMap<Integer, ListNode>();
this.head = new ListNode(-1, -1);
this.tail = new ListNode(-1, -1);
this.head.next = this.tail;
this.tail.pre = this.head;
}
}
public int get(int key) {
int value = -1;
if (this.keyNodeMap.containsKey(key)) {
ListNode tempNode = this.keyNodeMap.get(key);
value = tempNode.value;
tempNode.pre.next = tempNode.next;
tempNode.next.pre = tempNode.pre;
tempNode.next = head.next;
head.next.pre = tempNode;
tempNode.pre = head;
head.next = tempNode;
this.keyNodeMap.put(key, tempNode);
}
return value;
}
public void set(int key, int value) {
if (this.keyNodeMap.containsKey(key)) {
ListNode tempNode = this.keyNodeMap.get(key);
tempNode.value = value;
tempNode.pre.next = tempNode.next;
tempNode.next.pre = tempNode.pre;
tempNode.next = this.head.next;
this.head.next.pre = tempNode;
tempNode.pre = this.head;
this.head.next = tempNode;
this.keyNodeMap.put(key, tempNode);
return;
}
if (this.currentSize == this.capacity) {
// delete least recently used
ListNode delNode = this.tail.pre;
delNode.pre.next = delNode.next;
delNode.next.pre = delNode.pre;
this.keyNodeMap.remove(delNode.key);
this.currentSize --;
}
ListNode addNode = new ListNode(key, value);
addNode.pre = this.head;
addNode.next = this.head.next;
this.head.next.pre = addNode;
this.head.next = addNode;
this.keyNodeMap.put(key, addNode);
this.currentSize ++;
}
// 调试用,打印LRUCache中的元素
public void printElem(){
ListNode tempNode = this.tail.pre;
while (tempNode.key != -1) {
System.out.print(tempNode.key + " ");
tempNode = tempNode.pre;
}
System.out.println();
}
}