简述 LRU 算法
一种比较常见的缓存算法,也是内存管理使用的一种算法。在内存满的时候,选择内存中最近最久未使用的页面予以淘汰。
实现方式
哈希表 + 双向链表
双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
哈希表即为普通的哈希映射(ES6 Map),通过缓存数据的键映射到其在双向链表中的位置。
我们首先使用哈希表进行定位,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在 O(1) 的时间内完成 get 或者 put 操作。具体的方法如下:
对于 get 操作,首先判断 key 是否存在:
- 如果 key 不存在,则返回 -1−1;
- 如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。
对于 put 操作,首先判断 key 是否存在:
- 如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
- 如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。
var LRUCache = function (capacity) {
this.cache = new Map();
this.capacity = capacity;
};
LRUCache.prototype.get = function (key) {
if (this.cache.has(key)) {
// 存在即更新
let temp = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, temp);
return temp;
}
return -1;
};
LRUCache.prototype.put = function (key, value) {
if (this.cache.has(key)) {
// 存在即更新(删除后加入)
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 不存在即加入
// 缓存超过最大值,则移除最近没有使用的
this.cache.delete(this.cache.keys().next().value);
}
this.cache.set(key, value);
};