请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
示例:
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
hash+双链表实现LRU
解释:Hash的作用为在get()时快速获取数据,通过缓存数据的键映射到其在双链表中的位置,key是键值,value就是我们自己封装的数据node,如果能get到,说明此时有这个元素,返回node.value,如果不能,就返回-1。
双链表是用来实现对元素进行排序,按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,靠近尾部的键值对是最久未使用的。
具体操作:
对于get方法:
1. 首先判断key是否存在
2. 如果不存在就返回-1;
3. 如果存在,则将其移动到双链表的头部(具体做法为先删除当前结点,再将当前结点添加到双链表的头部)
对于put方法:
1. 首先判断key是否存在
2. 如果不存在,则创建新节点并添加到双链表的头部,在哈希表中添加对应的key和node,然后判断双链表的节点数是否超出容量,如果超出容量,则删除双链表中的尾部结点,并删除哈希表中对应的项。
3. 如果存在,则与get操作类似,只不过此时需要将对应的结点值更新,并将该结点移动到双链表的头部。
看容量为3的例子
具体代码如下
import java.util.HashMap;
import java.util.Map;
public class LRUCache {
class Node{
int key;
int value;
Node pre;
Node next;
public Node(){
}
public Node(int key,int value){
this.key=key;
this.value=value;
}
}
private int capacity;
private int size;
private Map<Integer,Node> cache=new HashMap<>();
private Node head,tail;
public LRUCache(int capacity) {
this.capacity=capacity;
this.size=0;
head=new Node();
tail=new Node();
head.next=tail;
tail.pre=head;
}
public int get(int key) {
Node getNode=cache.get(key);
if (getNode==null){
return -1;
}
moveToHead(getNode);
return getNode.value;
}
private void moveToHead(Node node) {
removeNode(node);
addToHead(node);
}
private void addToHead(Node node) {
node.next=head.next;
head.next.pre=node;
node.pre=head;
head.next=node;
}
private void removeNode(Node node) {
node.pre.next=node.next;
node.next.pre=node.pre;
}
public void put(int key, int value) {
Node getNode=cache.get(key);
if (getNode!=null){
getNode.value=value;
moveToHead(getNode);
}else {
Node node=new Node(key,value);
cache.put(key,node);
addToHead(node);
size++;
if (size>capacity){
cache.remove(tail.pre.key);
removeNode(tail.pre);
size--;
}
}
}
}