Const
题目要求get和put时间复杂度在O(1)。那么需要找到符合get或者put复杂度在O(1)内,符合这样的常见结构有链表、哈希表、数组,同时满足的只有map。但是map没法表达"最久没用"这个概念,同时我们还需要在超出容量的时候O(1)删除最久没用的key,因此我们想到双链表:
注意:对于双链表,在进行get或者put操作后,需要更新相关节点,让其处于链表头部表示最近使用了,它应该从尾部删除,因为它不是最久没使用的了。
整体方案:实现新数据结构的"增、删、查、改",利用哈希表+双链表实现这个数据结构。
class Node {
constructor(
public key: number,
public val: number,
public next: Node | null = null,
public prev: Node | null = null,
) { }
}
class LinkedList {
private dummy: Node;
constructor() {
this.dummy = new Node(-1, -1);
this.dummy.prev = this.dummy;
this.dummy.next = this.dummy;
}
get last() {
return this.dummy.prev;
}
addNodeByKey(key: number, val: number): Node {
return this.addNode(new Node(key, val));
}
// 头插法,把新节点放到最前面(dummy后面)
addNode(node: Node): Node {
// 记住当前的位置
const dummy = this.dummy;
const oldFirst = this.dummy.next!;
// 把新节点放到最前面(dummy后面)
node.prev = dummy;
dummy.next = node;
// 把新节点和原来的第一个节点连起来(这个部分做到了双链表的循环)
node.next = oldFirst;
oldFirst.prev = node;
return node;
}
delNode(node: Node): void {
node.prev!.next = node.next;
node.next!.prev = node.prev;
}
}
class LRUCache {
private nodeList: LinkedList = new LinkedList();
private keyToNode: Map<number, Node> = new Map();
constructor(private capacity: number) { }
/**
* O(1)
* 如果key存在,返回key对应的value
* 如果key不存在,返回-1
*/
get(key: number): number {
const node = this.getNode(key);
return node ? node.val : -1;
}
/**
* 如果key存在,变更key对应的value;
* 如果key不存在,插入key。
*/
put(key: number, val: number): void {
const node = this.getNode(key);
// 如果key存在,变更key对应的value
if (node) {
node.val = val;
}
else {
this.addNode(key, val);
}
}
protected addNode(key: number, val: number): Node {
// 如果key不存在,插入key。(插入之前,如果插入的时候超出了容量capacity,那么应该放弃最久没使用的key)
// 如果插入的时候超出了容量capacity,应该放弃最久没使用的key。涉及的两个结构都要删除key相关的内容
if (this.keyToNode.size === this.capacity) {
this.delNotUsed();
}
const node = this.nodeList.addNodeByKey(key, val);
this.keyToNode.set(key, node);
return node;
}
protected getNode(key: number): Node | undefined {
const node = this.keyToNode.get(key);
if (node) {
this.markUsed(node);
}
return node;
}
protected markUsed(node: Node): void {
this.nodeList.delNode(node);
this.nodeList.addNode(node);
}
protected delNotUsed(): void {
const node = this.nodeList.last;
if (node) {
this.nodeList.delNode(node);
this.keyToNode.delete(node.key);
}
}
}
/**
* 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)
*/
146.LRU 缓存
请你设计并实现一个满足 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:
输入:
["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]
解释:
提示:
1 <= capacity <= 3000
0 <= key <= 10^4
0 <= value <= 10^5
2 * 10^5
次 get 和 put