运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) -
如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
思路一:
通过一个哈希表,可以实现get和put时间复杂度为O(1)。
此时,利用java的LinkedHashMap可以实现有序的HashMap,可以设定其按照插入顺序或者访问顺序排序,按照访问顺序来排序恰好满足本题的要求。
- 时间复杂度:O(1),有序字典中所有操作均为O(1)。
- 空间复杂度:O(capicity),因为空间只用于有序字典存储最多 capacity + 1 个元素。
class LRUCache extends LinkedHashMap<Integer,Integer>{
private int capacity;
public LRUCache(int capacity) { //capicity为HashMap初始容量
super(capacity,0.75F,true); //accessOrder设置为true意味着按访问顺序排序
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key,-1); //获取不到则返回-1
}
public void put(int key, int value) {
super.put(key,value);
}
/**
*重写父类的方法,在size()>capicity时删除最老的Entry
**/
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest){
return size()>capacity;
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
}
思路二:
用别人的数据结构不能显示水平。所以可以自己实现一个LinkedHashMap。
LinkedHashMap的原理就是一个HashMap加一个双向链表。哈希表用来根据key找到对应节点,双向链表用来表示顺序。
注意:哈希表中存放的是key和对应的节点,value值存放在节点中。
时间空间复杂度和思路一一样。
class LRUCache {
private int capacity;
private int size;
private DLinkNode head,tail; //双向链表的伪头尾节点
private HashMap<Integer,DLinkNode> map;
class DLinkNode{ //双向链表的节点类
int key;
int value;
DLinkNode prev;
DLinkNode next;
}
//插入节点,始终插在双向链表头节点后
public void addNode(DLinkNode node){
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
//删除某个节点
public void removeNode(DLinkNode node){
node.prev.next = node.next;
node.next.prev = node.prev;
}
//将某个节点移动到头节点后
public void moveToAhead(DLinkNode node){
removeNode(node);
addNode(node);
}
//去掉最尾部的节点,并返回,用以在map中删除它
public DLinkNode popTail(){
DLinkNode res = tail.prev;
removeNode(tail.prev);
return res;
}
//LRUCache的构造函数
public LRUCache(int capacity) {
this.capacity = capacity;
size = 0;
head = new DLinkNode();
tail = new DLinkNode();
head.next = tail; //头尾互相指
tail.prev = head;
map = new HashMap<Integer,DLinkNode>();
}
public int get(int key) {
DLinkNode node = map.get(key);
if(node==null){
return -1;
}
moveToAhead(node); //get到之后放到头部
return node.value;
}
public void put(int key, int value) {
//先判断是否已经存在
if(map.get(key)==null){ //当不存在时,新建一个放进去
DLinkNode node = new DLinkNode();
node.key = key;
node.value = value;
addNode(node);
map.put(key,node);
size++;
if(size>capacity){ //是否已经满了
DLinkNode tailNode = popTail();
map.remove(tailNode.key);
size--;
}
}else{
DLinkNode node = map.get(key);
node.value = value;
map.put(key,node);
moveToAhead(node); //更新也要放到头部
}
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
代码很长,细节拉满,累死我了。