146. LRU缓存机制
题目描述
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得关键字 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得关键字 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
题解:双向链表+哈希
思路
- 双向链表及哈希表所需的节点类
- 构建双向链表,实现 addFirst、remove、removeLast、size 四个api
- 实现LRU
- get :判断是否已存在,不存在再调用put将数据提前
- put:
- 存在节点,删除旧的,新的插到头部
- 不存在:若缓存已满,删除最后一个。再加入数据,否则直接加入数据。
//创建双链表,哈希表所需结点
class Node{
int key,val;
public Node pre,next;
public Node(int k,int v){
this.key = k;
this.val = v;
}
}
class DoubleList{
private Node head,tail; //头尾虚结点
private int size; //元素数
public DoubleList(){
head=new Node(0,0);
tail=new Node(0,0);
head.next = tail;
tail.pre = head;
size=0;
}
//在链表头部添加节点x
public void addFirst(Node x){
x.next = head.next;
x.pre =head;
head.next.pre = x;
head.next = x;jie
size++;
}
//删除链表中的x结点(x一定存在)
public void remove(Node x){
x.pre.next=x.next;
x.next.pre = x.pre;
size--;
}
//删除最后一个节点并返回
public Node removeLast(){
if(tail.pre==head) return null;
Node last = tail.pre;
remove(last);
return last;
}
public int size(){ return size;}
}
class LRUCache {
private Map<Integer, Node> map;
private DoubleList cache;
private int cap;
public LRUCache(int capacity) {
this.cap = capacity; //最大容量
map = new HashMap<>();
cache = new DoubleList();
}
public int get(int key) {
if(!map.containsKey(key)) return -1;
int val = map.get(key).val;
//利用put方法将该数据提前
put(key,val);
return val;
}
public void put(int key, int value) {
//制作新节点x
Node x = new Node(key,value);
if(map.containsKey(key)){
//删除旧节点
cache.remove(map.get(key));
//添加到头部
cache.addFirst(x);
//更新map中的值
map.put(key,x);
}
else{
if(cache.size()==cap){
//删除最后一个数据
Node last = cache.removeLast();
//移除map中对应的键值对
map.remove(last.key);//需要对应的key,所以cache存储需要用Node,而不是只存value
}
//将新节点直接添加头部
cache.addFirst(x);
//更新map
map.put(key,x);
}
}
}