题目来源


代码

java
class LRUCache {
//双向链表结点
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
DLinkedNode() {};
DLinkedNode(int key, int value) {this.key = key; this.value = value;}
}
private DLinkedNode head, tail; // 虚拟头尾指针
// 哈希表 做缓存
private Map<Integer, DLinkedNode> cache = new HashMap<>();
private int size; //当前缓存大小
private int capacity; // 缓存容量大小
public LRUCache(int capacity) {
this.capacity = capacity;
size = 0;
//创建双向链表
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
//缓存中无对应结点
if(node == null) return -1; //返回-1
//有
moveToHead(node); //更新使用状态顺序
return node.value; //返回结点的value值
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
//缓存中无对应结点
if(node == null) {
// 创建结点插入链表 + 添加至缓存
node = new DLinkedNode(key, value);
size ++;
addToHead(node); //头插法
cache.put(key, node);
//检查缓存容量是否足够
if(size > capacity) {
// 移除链表尾部元素 + 清理缓存
DLinkedNode tailNode = removeTail();
cache.remove(tailNode.key); // 需要尾部结点key
size --;
}
} else {
// 有
node.value = value; //修改值
moveToHead(node); //更新结点使用状态顺序
}
}
// 双向链表基本操作 -> 前插头结点
private void addToHead(DLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
// 双向链表基本操作 -> 删除某个结点
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
//java会自动识别并释放结点空间
}
// 双向链表衍生操作 -> 将结点移到头部更新使用状态(删除结点+前插结点)
private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
}
// 双向链表衍生操作 -> 删除并返回尾部结点
private DLinkedNode removeTail() {
DLinkedNode tailNode = tail.prev;
removeNode(tailNode);
return tailNode;
}
}
/**
* 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);
*/
代码分析
总的来说,就是 通过哈希表 映射到 双向链表某个结点。双向链表作为实际数据存储结构。
也就是说,写双向链表。
数据结构选择原因
哈希表用于存储数据, 通过key -> 对应数据 。
但是需要考虑到使用状况顺序问题(近期操作时间先后顺序排序),使用双向链表,存储数据。
于是 哈希表中, key -> DLinkedNode(双向链表的结点)
写代码思路
双向链表结点类
创建哈希表
LRU缓存初始化 -> 创建双向链表
LRU缓存的get方法 -> 从哈希表中获取 对应结点
== 需要写 双向链表的更新使用状态顺序的方法。moveToHead
LRU缓存的put方法 -> 从哈希表中获取 对应结点 + 数据插入或修改
数据插入也就是 创建结点,插入结点到链表
== 需要写 双向链表头插法addToHead 方法 and 双向链表删除尾结点方法 removeTail and 双向链表删除某个结点方法 removeNode
数据修改就是对应结点 value值赋值。