题目描述:
请你设计并实现一个满足 [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)
的平均时间复杂度运行。
思路:图片源自灵茶山艾府
解法1:使用LinkedHashMap
代码如下:
//使用LinkedHashMap 实现LRU,
//思路:插入时,如果存在相同旧key则将旧key删除,在插入新的key value,如果不存在则直接插入。
//如果超过了容量,则删除第一个元素
private HashMap<Integer, Integer> hashMap = new LinkedHashMap<Integer, Integer>();
private int capacity;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
//删除并重新插入
Integer value = hashMap.remove(key);
if (value != null) {// key 在 cache 中
hashMap.put(key, value);// 把 key 移到链表末尾
return value;
}
// key 不在 cache 中
return -1;
}
public void put(int key, int value) {
// 删除 key,并利用返回值判断 key 是否在 cache 中
if (hashMap.remove(key) != null) {// key 在 cache 中
hashMap.put(key, value);// 把 key 移到链表末尾
return;
}
// key 不在 cache 中,那么就把 key 插入 cache,插入前判断 cache 是否满了
if (hashMap.size() == capacity) {// cache 满了
//获取最久未使用的key,keySet()返回的是一个Set,所以用iterator() 获取第一个元素
Integer tmp = hashMap.keySet().iterator().next();
hashMap.remove(tmp);// 移除最久未使用 key
}
hashMap.put(key, value);
}
解法2:手写双向链表+HashMap
代码如下:
// 双向链表
private static class Node {
int key;
int value;
Node prev;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
private final int capacity;
// 哨兵节点
private final Node dummy = new Node(-1, -1);
// 用于判断节点是否存在
private final HashMap<Integer, Node> map = new HashMap<>();
public LRUCache(int capacity) {
this.capacity = capacity;
dummy.prev = dummy;
dummy.next = dummy;
}
public int get(int key) {
// 获取key对应的节点
Node node = getNode(key);
// 如果节点不存在,则返回-1
return node == null ? -1 : node.value;
}
public void put(int key, int value) {
// 获取key对应的节点
Node node = getNode(key);
// 节点存在,则更新节点的值
if (node != null) {
node.value = value;
return;
}
// 节点不存在,则创建节点
node = new Node(key, value);
// 将节点加入到map中
map.put(key, node);
// 将节点加入到链表头部
pushFront(node);
// 如果容量满了,则删除链表尾部节点
if (map.size() > capacity) {
// 删除链表尾部节点
Node backNode = dummy.prev;
// 从map中删除节点
remove(backNode);
map.remove(backNode.key);
}
}
// 获取key对应的节点,同时将节点移动到链表头部
private Node getNode(int key) {
// 如果不存在,则返回null
if (!map.containsKey(key)) {
return null;
}
// 获取节点
Node node = map.get(key);
// 删除节点
remove(node);
// 将节点移动到链表头部
pushFront(node);
return node;
}
// 删除节点
private void remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
// 将节点插入到链表头部
private void pushFront(Node node) {
node.next = dummy.next;
node.prev = dummy;
node.prev.next = node;
node.next.prev = node;
}