请你设计并实现一个满足 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
是最多缓存的元素数