LRU Cache
LRU Cache介绍
关于此缓存机制的介绍,参考国外大佬的文章
LRU Cache(Least Recently Used cache)是最流行的缓存驱逐策略之一。同时也是一个非常常见的面试问题。
LRU 策略
LRU Cache会跟踪缓存中的项目的被访问顺序。按照请求顺序存储项目。因此,最近使用的项目将位于缓存的顶部,而最近最少用的项目将位于缓存的尾部。在LRU的策略之中,当缓存已满时,最长时间未使用的项目将被淘汰逐出缓存。
依照上述。LRU缓存的原理、思路其实很简单,如果缓存的容量是n,意味着最近使用的n个或n个以内的项目存储于该缓存中。当缓存占满,每次添加新项目,都会把最末尾的项目删去,再往顶部放入最新项目。
举例
为了更加方便的看出对缓存的数据的储存以及淘汰等操作,举例说明
首先给定容量为3的一个LRU缓存空间,然后依次请求访问也即压入数据A、B、C。我们可以看到缓存的变化。缓存从顶至底放入数据,由于C最后放入,所以位于缓存最顶部。
接着,请求访问B,B便会被至于缓存的顶部,表示最近访问
接着是请求访问D,于是缓存就放入了数据D,在放入之前要把最底端的数据移除,即A移除,以此保证LRU容量的恒定。
LRU的工作原理上面例子就清楚了,接下来看如何实现
实现LRU Cache
对于LRU缓存的实现,就是leetcode上的这道题。
说一下思路:
题目谈到key-value(关键字和值)的存在,让我们想起了java的hashmap和python的dict
都是以键值对的形式储存
先说hashmap,由于hashmap无法记录访问数据项目访问的前后顺序,这里考虑使用双链表来记录顺序。
用链表不用数组是因为对数据修改的时间复杂度为O(n)
使用hashmap和双链表结合就避免了这个问题。
代码如下:
class Node {
int key;
int val;
Node prev;
Node next;
public Node(int key,int value) {
this.key = key;
this.val = value;
}
}
class LRUCache{
int capacity;
Map<Integer,Node> map = new HashMap<>();
Node head = new Node(0, 0);
Node tail = new Node(0, 0);
public LRUCache(int capacity) {
this.capacity = capacity;
head.next = tail;
tail.prev = head;
}
public void put(int key,int value) {
if (!map.containsKey(key)) {
if (map.size() == capacity) deletLastNode();
Node newNode = new Node(key, value);
newNode.prev = head;
newNode.next = head.next;
head.next.prev = newNode;
head.next = newNode;
map.put(key,newNode);
}else {
Node node = map.get(key);
node.val = value;
moveNodeToTop(node);
}
}
public int get(int key) {
if (map.containsKey(key)) {
Node node = map.get(key);
moveNodeToTop(node);
return node.val;
}else {
return -1;
}
}
private void deletLastNode() {
Node node = tail.prev;
node.prev.next = tail;
tail.prev = node.prev;
map.remove(node.key);
}
private void moveNodeToTop(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
}
python就不需要像这样用哈希表和链表结合,python有字典而且是有序字典
用orderedDict()作为LRU缓冲区
利用orderedDict()的特殊方法popitem()可以实现对最前或者最后数据的操作
popitem()的选项last为ture时表示的是栈状态,把刚放入的pop出去;last为false表示的是队列的状态,把最早放入的pop出去
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity:int):
self.capacity = capacity
self.cache = OrderedDict()
self.size = 0
def get(self,key:int) -> int:
if key in self.cache:
value = self.cache[key]
del self.cache[key]
self.cache[key] = value
return value
else:
return -1
def put(self, key:int, value:int) ->None:
if key in self.cache:
del self.cache[key]
self.cache[key] = value
elif self.size >= self.capacity:
self.cache.popitem(last=False)
else:
self.size +=1
self.cache[key] = value