请你设计并实现一个满足 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)的平均时间复杂度运行。
解法1 利用ES6有序Map特性
思路
JavaScript 的 Map 本身就是一个有序 Map —— 它会按照插入顺序保存 key。
所以可以利用这个特性来去构造 LRU。当访问时,将它先取出来,然后再插入这样就模拟了最新访问,其实就是把 key 重新插入到了末尾。
而删除中 map.keys().next().value 是最早插入的 key(也就是最久未使用的 key)。
代码
class LRUCache {
public map = new Map();
public capacity = 0;
constructor(capacity: number) {
this.capacity = capacity;
}
get(key: number): number {
if (this.map.has(key)) {
let value = this.map.get(key)
this.map.delete(key)
this.map.set(key, value)
return value
}
return -1;
}
put(key: number, value: number): void {
if (this.map.has(key)) {
this.map.delete(key)
}
this.map.set(key, value)
if (this.map.size > this.capacity) {
this.map.delete(this.map.keys().next().value)
}
}
}
时空复杂度
时间复杂度:
1.get(key) O(1):均摊Map 查找 + delete + set(实际上都是哈希表操作)
2.put(key, val O(1):均摊 删除旧 key + set + 如果满了再删最老的 key
3.map.keys().next().value O(1) 取第一个键是常数时间(JS Map 实现)
空间复杂度:O(n),最多 n 个 key-value
解法2 map+双链表
思路
刚刚利用 ES6 的特性只是因为 map 的有序特性,并不会在其他语言中通用。
而通过 map 和双向链表则是更像一个工具类的标准解决方案。
代码
class DLinkedNode {
key: number;
value: number;
prev: DLinkedNode | null = null;
next: DLinkedNode | null = null;
constructor(key = 0, value = 0) {
this.key = key;
this.value = value;
}
}
class LRUCache {
private cache: Map<number, DLinkedNode>;
private capacity: number = 0;
private size: number;
private head: DLinkedNode;
private tail: DLinkedNode;
constructor(capacity: number) {
this.capacity = capacity;
this.cache = new Map();
this.size = 0;
this.head = new DLinkedNode();
this.tail = new DLinkedNode();
this.head.next = this.tail;
this.tail.prev = this.head;
}
get(key: number) {
const node = this.cache.get(key);
if (!node) return -1;
this.moveToHead(node);
return node.value;
}
put(key: number, value: number) {
const node = this.cache.get(key);
if (node) {
node.value = value;
this.moveToHead(node);
} else {
const newNode = new DLinkedNode(key, value);
this.cache.set(key, newNode);
this.addToHead(newNode);
this.size++;
if (this.size > this.capacity) {
const tail = this.removeTail();
if (tail) {
this.cache.delete(tail.key);
this.size--;
}
}
}
}
private addToHead(node) {
node.prev = this.head;
node.next = this.head.next;
if (this.head.next) this.head.next.prev = node;
this.head.next = node;
}
private removeNode(node) {
const prev = node.prev;
const next = node.next;
if (prev) prev.next = next;
if (next) next.prev = prev;
}
private moveToHead(node) {
this.removeNode(node);
this.addToHead(node);
}
private removeTail() {
const node = this.tail.prev;
if (node && node !== this.head) {
this.removeNode(node);
return node;
}
return null;
}
}
时空复杂度
时间复杂度:
1.get(key) O(1): Map 查找节点 + 移动到头部(链表操作)
2.put(key, val) O(1): Map 查找 + 插入/更新链表 + 删除尾部(如有)
3.delete最老节点 O(1): 链表尾部删除 + Map 删除
空间复杂度:O(n),n 是最多缓存的元素数