LeetCode 146. LRU 缓存机制
题目:中等
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
- LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
- int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
- void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
链接:https://leetcode-cn.com/problems/lru-cache
思路
方式一:哈希表+双向链表
- 因为LRU涉及排序后的定位,及删除与插入,因此用HashMap在O(1)时间复杂度下完成元素定位,用双向链表在O(1)时间复杂度下完成元素的删除与插入
- 双向链表:按照被使用顺序存储Node,靠近头部为最近使用的(Node中封装了key value 字段)
- HashMap:通过键映射到Node
- 流程:先使用哈希表进行定位,找出Node,随后将其移动到双向链表头部
特别地,在双向链表中可以使用伪头部和伪尾部标记界限,这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在
class LRUCache {
HashMap<Integer,Node> map;
DoubleLinkedList list;
int cap;
public LRUCache(int capacity) {
map = new HashMap<>();
list = new DoubleLinkedList();
this.cap = capacity;
}
public int get(int key) {
if(!map.containsKey(key)) return -1;
int val = map.get(key).val;
put(key,val);
return val;
}
public void put(int key, int value) {
Node newNode = new Node(key,value);
if(map.containsKey(key)){
list.delete(map.get(key));
list.addFirst(newNode);
map.put(key,newNode);
}else{
if(map.size() == cap){
int k = list.deleteLast();
map.remove(k);
}
map.put(key,newNode);
list.addFirst(newNode);
}
}
}
class Node{
int key;
int val;
Node prev;
Node next;
public Node(int key,int val){
this.key = key;
this.val = val;
}
}
class DoubleLinkedList{
Node head;
Node tail;
public DoubleLinkedList(){
head = new Node(0,0);
tail = new Node(0,0);
head.next = tail;
tail.prev = head;
}
public void addFirst(Node newNode){
newNode.prev = head;
newNode.next = head.next;
head.next.prev = newNode;
head.next = newNode;
}
public int delete(Node n){
int key = n.key;
n.next.prev = n.prev;
n.prev.next = n.next;
return key;
}
public int deleteLast(){
if(head.next == tail) return -1;
return delete(tail.prev);
}
}
/**
* 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);
*/
**方式二:直接用java自带的LinkedHashMap
class LRUCache {
int cap;
Map<Integer,Integer> map;
public LRUCache(int cap){
this.cap = cap;
this.map = new LinkedHashMap<>(cap,0.75f,true){//第一个参数代表起始容量,默认为1,若小于1,底层会将容量左移,直到大于默认容量,第二个参数为加载因子,true表示记录访问顺序
/**
* 通过如下方法的返回值告诉map对象,是否要移出元素.
*1)true表示移出
*2)false表示不移出
* 说明:此方法会在put方法执行时调用.
*/
protected boolean removeEldestEntry(Map.Entry eldest){
return map.size()>cap;
}
};
}
public int get(int key){
return map.getOrDefault(key,-1);
}
public void put(int key,int val){
map.put(key,val);
}
}```