每天一道算法题,LeetCode已经从国服100跌倒4257名了。再复习一波,记录经典算法。
实现 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)
的平均时间复杂度运行。
LRU缓存工作中用的一定不少,不过都是现成的库,比如DiskLruCache。很少有人能手撸一个LRU缓存,看源码基本上也是走马观花了解一些。但LRU偏偏是拼多多、字节喜欢考察的算法类型。
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
LRU 缓存机制可以通过哈希表辅以双向链表实现,用一个哈希表和一个双向链表维护所有在缓存中的键值对辅助完成:
class LRUCache {
static class Node{
int key = -1;
int val = -1;
Node pre = null;
Node next = null;
public Node(){
}
public Node(int key, int val){
this.key = key;
this.val = val;
}
}
Node dummyHead;
Node dummyTail;
Map<Integer,Node> cache = new HashMap<>();
private int capacity;
private int size;
public LRUCache(int capacity) {
this.capacity = capacity;
size = 0;
dummyHead = new Node();
dummyTail = new Node();
dummyHead.next = dummyTail;
dummyTail.pre = dummyHead;
}
public int get(int key) {
if(!cache.containsKey(key)){
return -1;
}
Node node = cache.get(key);
unlink(node);
addHead(node);
return node.val;
}
public void put(int key, int value) {
Node cur = new Node(key,value);
if(cache.containsKey(key)){
Node node = cache.get(key);
node.val = value;
unlink(node);
addHead(cur);
}else{
if(size == capacity){
addHead(cur);
removeTail();
}else{
addHead(cur);
}
}
}
private void unlink(Node node){
node.pre.next = node.next;
node.next.pre = node.pre;
--size;
}
private void addHead(Node node){
node.next = dummyHead.next;
dummyHead.next.pre = node;
dummyHead.next = node;
node.pre = dummyHead;
++size;
cache.put(node.key,node);
}
private void removeTail(){
Node tail = dummyTail.pre;
unlink(tail);
cache.remove(tail.key);
}
}