题目
请你设计并实现一个满足 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) 的平均时间复杂度运行。
解法
使用map+双链表,使用双链表是因为方便删除末尾节点。
public class LRUCache {
private Map<Integer, LinkedNode> cache;
private int capacity;
private int size = 0;
// 用于判断哪个node是最近最少使用的:头部为最近使用的,尾部为最远使用的
private LinkedNode head, tail;
class LinkedNode {
int key;
int value;
LinkedNode prev;
LinkedNode next;
public LinkedNode() {}
public LinkedNode(int key, int value) {
this.key = key;
this.value = value;
}
}
// 初始化容器
public LRUCache(int capacity) {
cache = new HashMap();
// 缓存长度
this.capacity = capacity;
head = new LinkedNode();
tail = new LinkedNode();
head.next = tail;
tail.prev = head;
}
/**
* 获取缓存
* @param key
*/
public int get(int key) {
// 从缓存中获取key
LinkedNode node = cache.get(key);
// 没有获取到key
if (node == null) {
return -1;
}
// 从链表中删除原node
removeNode(node);
// 将node添加在链表头
addToHead(node);
return node.value;
}
/**
* 添加缓存
* @param key
* @param value
*/
public void put(int key, int value) {
// 从缓存中获取key
LinkedNode node = cache.get(key);
// 没有获取到key
if (node == null) {
// 创建新node
node = new LinkedNode(key, value);
// 先将size扩展
size++;
// 越界需要删除尾部节点
if (size > capacity) {
// 删除队尾节点
LinkedNode tailNode = removeTailNode();
// 将队尾节点从map中删除
cache.remove(tailNode.key);
// size减少
size--;
}
// 将node放在链表头部
addToHead(node);
// 将链表加入map
cache.put(key, node);
} else {
// 更新node的value值
node.value = value;
// 删除链表中原node
removeNode(node);
// 将新node放在链表头部
addToHead(node);
// 将链表加入map
cache.put(key, node);
}
}
public void addToHead(LinkedNode node) {
// 置于链表头
head.next.prev = node;
node.next = head.next;
node.prev = head;
head.next = node;
}
public void removeNode(LinkedNode node) {
if (node != null) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
}
public LinkedNode removeTailNode() {
LinkedNode removeNode = tail.prev;
removeNode(tail.prev);
return removeNode;
}
}