要实现get和put时间复杂度都为O(1),能想到的就是哈希表。由于要实现LRU,想到的就是链表,每次get/put,就把对应结点放到链表头部,同时该结点原本的prev和next都能接上,因此这条链表一定是一条双向链表。代码实现难点在于处理边界情况,即对应结点的prev或next为null时怎么处理,还有当head和tail为null时怎么处理等等。我们可以在纸上把这些情况都画一下,帮助我们在写代码时思路更清晰。
class LinkNode {
key: number;
val: number;
prev: LinkNode;
next: LinkNode;
constructor(key:number, val:number) {
this.key = key;
this.val = val;
this.prev = this.next = null;
}
};
class LRUCache {
head: LinkNode | null;
tail: LinkNode | null;
cap: number;
key_node: Map<number,LinkNode>;
constructor(capacity: number) {
this.cap = capacity;
this.key_node = new Map();
this.head = this.tail = null;
}
get(key: number): number {
if (this.key_node.has(key)) {
let node = this.key_node.get(key);
let prev = node.prev, next = node.next;
if (prev) {
prev.next = next;
node.prev = null;
node.next = this.head;
this.head.prev = node;
this.head = node;
if (!next) this.tail = prev;
else next.prev = prev;
}
return node.val;
} else return -1;
}
put(key: number, value: number): void {
if (this.key_node.has(key)) {
let node = this.key_node.get(key);
node.val = value;
let prev = node.prev, next = node.next;
if (prev) {
prev.next = next;
node.next = this.head;
node.prev = null;
this.head.prev = node;
this.head = node;
if (!next) this.tail = prev;
else next.prev = prev;
}
} else {
if (this.key_node.size == this.cap) {
let node_toRemove = this.tail;
this.key_node.delete(node_toRemove.key);
this.tail = node_toRemove.prev;
if (this.tail) this.tail.next = null;
if (this.head.key == node_toRemove.key) this.head = null;
}
let node = new LinkNode(key, value);
this.key_node.set(key, node);
if (this.head) {
node.next = this.head;
this.head.prev = node;
this.head = node;
} else {
this.head = this.tail = node;
}
}
}
}