一. 题目
请你设计并实现一个满足 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) 的平均时间复杂度运行。
示例:
输入
["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
提示:
1 <= capacity <= 30000 <= key <= 100000 <= value <= 105- 最多调用
2 * 105次get和put
二. 解析
解法一:
利用Map有序这一特性
get:如果key已经存在,那就获取并缓存当前value,删除这个key-value pair,然后set进去,就会进入Map的末尾;如果不存在则按题目要求返回 -1put:如果key已经存在,那就直接删除这个key-value pair,然后set到末尾;如果不存在,则直接set到末尾,然后检查是否超过capacity,如果超过了,就删除Map的第一个pair
代码如下:
/**
* @param {number} capacity
*/
var LRUCache = function (capacity) {
this.capacity = capacity;
this.map = new Map();
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function (key) {
// 如果没有,返回-1
if (!this.map.has(key)) return -1;
// 如果有
// 获取保存这个value
const value = this.map.get(key);
// 删除这个key-value pair
this.map.delete(key);
// set到Map末尾
this.map.set(key, value);
// 返回这个value
return value;
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function (key, value) {
// 如果有,则删除
if (this.map.has(key)) {
this.map.delete(key);
}
// 无论有没有,都set到末尾
this.map.set(key, value);
// 判断是否超过容量
if (this.map.size > this.capacity) {
// 如果超过,删除首位
this.map.delete(this.map.keys().next().value);
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* var obj = new LRUCache(capacity)
* var param_1 = obj.get(key)
* obj.put(key,value)
*/
另外,这里用到了Map.prototype.keys(),它返回一个迭代器(iterator),对于迭代器可以参考其他的文章。
解法二:
对于这个o(1)的时间复杂度要求,我们也可以手动实现双向链表来解决,不同的是把最近使用的放在最前面,然后我们有一个哨兵节点,它的prev指向相对末尾,它的next指向相对首位
对于Node
class Node {
constructor(key = 0, value = 0) {
this.key = key;
this.value = value;
// 向前的指针
this.prev = null;
// 向后的指针
this.next = null;
}
}
在LRUCache class里,构造器
constructor(capacity) {
// 容量
this.capacity = capacity;
// 哨兵节点
this.dummy = new Node();
this.dummy.prev = this.dummy;
this.dummy.next = this.dummy;
// 维护一个Map用于存储key和对应的node
this.keyNode = new Map();
}
这里维护三个方法,用于删除节点、把节点插入首位,获取节点(注意这里是获取节点而不是节点里的value)
#remove(x) {
x.prev.next = x.next;
x.next.prev = x.prev;
}
#pushFront(x) {
x.prev = this.dummy;
x.next = this.dummy.next;
x.prev.next = x;
x.next.prev = x;
}
#getNode(key) {
if (!this.keyNode.has(key)) return null;
const node = this.keyNode.get(key);
this.#remove(node);
this.#pushFront(node);
return node;
}
再然后是所需的get和put方法了,思想跟方法一是类似的。
get(key) {
const node = this.#getNode(key);
return node ? node.value : -1;
}
put(key, value) {
let node = this.#getNode(key);
if (node) {
node.value = value;
return;
}
node = new Node(key, value);
this.keyNode.set(key, node);
this.#pushFront(node);
if (this.keyNode.size > this.capacity) {
const backNode = this.dummy.prev;
this.keyNode.delete(backNode.key);
this.#remove(backNode);
}
}
完整代码:
class Node {
constructor(key = 0, value = 0) {
this.key = key;
this.value = value;
this.prev = null;
this.next = null;
}
}
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.dummy = new Node();
this.dummy.prev = this.dummy;
this.dummy.next = this.dummy;
this.keyNode = new Map();
}
#remove(x) {
x.prev.next = x.next;
x.next.prev = x.prev;
}
#pushFront(x) {
x.prev = this.dummy;
x.next = this.dummy.next;
x.prev.next = x;
x.next.prev = x;
}
#getNode(key) {
if (!this.keyNode.has(key)) return null;
const node = this.keyNode.get(key);
this.#remove(node);
this.#pushFront(node);
return node;
}
get(key) {
const node = this.#getNode(key);
return node ? node.value : -1;
}
put(key, value) {
let node = this.#getNode(key);
if (node) {
node.value = value;
return;
}
node = new Node(key, value);
this.keyNode.set(key, node);
this.#pushFront(node);
if (this.keyNode.size > this.capacity) {
const backNode = this.dummy.prev;
this.keyNode.delete(backNode.key);
this.#remove(backNode);
}
}
}