实现思路
1 方法1: 循环链表 + Map
1.1 get:
- 需要一个 MyNode,来存储 { key: val }
- 需要一个 Map, 来O(1)判断 是否存在;存在 ? node.val : -1
- 如果node存在, 需要把它移动到最前面:moveFirst
- moveFirst 可以被拆解/转化为是:delNode + addFirst
1.2 put:
- 需要一个Map, 来O(1)判断 是否存在:
- 存在: 更新 node.val + moveFirst + 对应Map记录值 更新
- 不存在: 新构造 MyNode实例后,addFirst + 创建 对应Map记录值
- 如果此时 Map.size > Cap, 则要:
- 删除最后一个接口:delLast,可以看做是特定位置的 delNode
- 同时还要删除掉 Map里 对应的LastNode记录: 即通过node.key 反删 对应Map
1.3 由上可知,我们需要辅助的操作有3个:
- moveFirst: 又可以被拆解/转化为: delNode + addFirst
- addFirst: 单向链表,就能实现是O(1)的
- delNode(包括了delLast): 想在O(1)内实现,就必须使用 双向/循环 列表了
1.4 易错点:
- 循环链表:要始终保证dummy.pre 指向最后一个节点
- 删除最后一个节点时,需要同时删除掉 Map里的记录
2 方法2 Map + Map.keys()的Iter有序性
2.1 利用了Map的有序性:
- ES6 Map的特性:Map会记住键的插入顺序
- Map.keys() 会返回一个 MapIterator 对象,它是一个迭代器,包含了 Map 中所有键的值,
它会按照插入顺序排列 - 所以只要通过先del再Set的操作,就能始终保证最右边的是最新的,最左边的是最旧的
参考文档
代码实现
1 方法1: 循环链表 + Map 时间复杂度: O(1); 空间复杂度(n)
class LRUCache {
record: Map<number, MyNode>;
cap: number;
link: dbLinked;
constructor(capacity: number) {
this.cap = capacity;
this.record = new Map();
this.link = new dbLinked();
}
get(key: number): number {
const node = this.record.get(key);
const res = node ? node.val : -1;
if (node) this.link.moveFirst(node);
return res;
}
put(key: number, value: number): void {
const node = this.record.get(key);
if (node) {
node.val = value;
this.link.moveFirst(node);
this.record.set(key, node);
} else {
const curNode = new MyNode(key, value);
this.link.addFirst(curNode);
this.record.set(key, curNode);
}
if (this.record.size > this.cap) {
// 易错点3: 删除最后一个节点时,需要同时删除掉 Map里的记录
const lastNode = this.link.delLast();
this.record.delete(lastNode.key);
}
}
}
class MyNode {
key: number;
val: number;
pre: MyNode | null;
next: MyNode | null;
// 易错点1: 循环链表
constructor(key = -1, val = -1) {
this.key = key;
this.val = val;
this.pre = this;
this.next = this;
}
}
class dbLinked {
dummy: MyNode;
constructor() {
this.dummy = new MyNode();
}
// moveFirst: delNode + addFirst
moveFirst(node) {
this.delNode(node);
this.addFirst(node);
}
addFirst(node) {
// 易错点2: 要始终保证dummy.pre 指向最后一个节点,而不是之前的头节点
// this.dummy.pre = this.dummy.next || node;
this.dummy.next.pre = node;
node.pre = this.dummy;
node.next = this.dummy.next;
this.dummy.next = node;
}
delNode(node) {
node.next.pre = node.pre;
node.pre.next = node.next;
node.pre = node.next = null;
}
delLast() {
const lastNode = this.dummy.pre;
// 空链表情况,此题默认保证不会出现这种情况
if (lastNode === this.dummy) return null;
this.delNode(lastNode);
return lastNode;
}
}
2 方法2:Map + Map.keys()的Iter有序性 时间复杂度: O(1); 空间复杂度(n)
class LRUCache {
cache: Map<number, number>;
cap: number;
constructor(capacity: number) {
this.cache = new Map();
this.cap = capacity;
}
get(key: number): number {
const val = this.cache.get(key);
if (val == null) return -1;
// 如果存在key, 则先del,再set,从而更新key的位置 到最新的
this.cache.delete(key);
this.cache.set(key, val);
return val;
}
put(key: number, value: number): void {
this.cache.delete(key);
this.cache.set(key, value);
if (this.cache.size > this.cap) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
}
}