Appearance
146.LRU 缓存
题目描述
请你设计并实现一个满足 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) 的平均时间复杂度运行。
思路分析
利用
双向链表
和哈希表
,实现O(1)
复杂度查询,插入,删除
需手写
双向链表
的实现对一个结点进行操作的时候,需要注意
双向链表
和哈希表
的数据同步问题详看代码注解
AC 代码
class LRUCache {
private int capacity;
private Map<Integer, Node> map = new HashMap<>();
private DoubleList cache = new DoubleList();
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
if(!map.containsKey(key)){
return -1;
}
int val = map.get(key).val;
makeRecently(key,val);
return val;
}
public void put(int key, int value) {
if(map.containsKey(key)){
makeRecently(key,value);
return;
}
if(cache.size() >= capacity){
Node first = cache.deleteFirstNode();
map.remove(first.key);
}
// 新建结点
Node newNode = new Node(key,value);
// cache中添加结点
cache.addNode(newNode);
// map中存储结点
map.put(key, newNode);
}
// 提升为最近使用,可能存在值更新的情况
public void makeRecently(int key,int val){
// 获取旧结点
Node node = map.get(key);
// 删除旧结点
cache.deleteNode(node);
// 生成新节点,判断与旧结点是否相同
Node newNode = val == node.val ? node : new Node(key,val);
// 添加新节点
cache.addNode(newNode);
// 如果与旧结点的值不相同,更新map中的值
if(val != node.val){
map.put(key,newNode);
}
}
// 定义结点
class Node{
public int key,val;
// 前后结点
public Node prev = null,next = null;
public Node(int key,int val){
this.key = key;
this.val = val;
}
}
// 定义双向链表
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.prev = head;
}
// 往尾部添加结点
public void addNode(Node node){
node.next = tail;
node.prev = tail.prev;
tail.prev.next = node;
tail.prev = node;
size++;
}
// 删除链表中结点x
public void deleteNode(Node node){
node.next.prev = node.prev;
node.prev.next = node.next;
size--;
}
// 删除链表中第一个结点
public Node deleteFirstNode(){
if(head.next == tail){
return null;
}
Node first = head.next;
deleteNode(first);
return first;
}
public int size(){
return size;
}
}
}