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) 的平均时间复杂度运行。
解:
- 使用哈希表记录数据,使用双向链表记录值的使用顺序,哈希表的key代表值的key,val代表值对应的节点。节点保存值的key和val
- 创建占位头尾节点,便于直接获取到此时的头尾节点,越接近头节点的就是越近使用的,反之就是越久不适用的
- 存值时如果表中无此数据,那么生成一个节点,根据头节点可以得到最近使用值的节点,把新生成的节点插入头节点和最近使用值节点之间。并把这个节点值更新。如果此时哈希表大小过量,那么把尾节点的上一个节点从链表中移除并把表中对应记录删除。
- 存值时如果表中有数据,那么取出哈希表中该key对应的节点并把val更新,把这个节点从原来位置脱离并插入头节点的后一位
- 取值时的移动节点操作和4步骤一样,然后把表中数据返回
class LRUCache {
private capacity: number
private hashMap: Map<number, any>
// 占位头节点,方便更新真正的头节点
private headNode: any
// 占位尾节点,方便找到真正的尾节点
private tailNode: any
constructor(capacity: number) {
this.capacity = capacity
this.hashMap = new Map()
this.headNode = this.node(0,0)
this.tailNode = this.node(0,0)
this.headNode.next = this.tailNode
this.tailNode.pre = this.headNode
}
moveNode (curNode: any): void {
const oldNode = this.headNode.next
if (oldNode === curNode) return
// 将cur脱离原来位置
curNode.pre.next = curNode.next
curNode.next.pre = curNode.pre
// 将cur放到链表头部
oldNode.pre = curNode
curNode.pre = this.headNode
this.headNode.next = curNode
curNode.next = oldNode
}
get(key: number): number {
const keyVal = this.hashMap.get(key)
if (keyVal) {
this.moveNode(keyVal)
return keyVal.val
}
return -1
}
put(key: number, value: number): void {
const keyVal = this.hashMap.get(key)
if (keyVal) {
// 获取双向链表中的节点
keyVal.val = value
// 更新节点位置
this.moveNode(keyVal)
} else {
// 创建一个新节点
const curNode = this.node(key, value)
// 把节点一起存入map,用来获取在双向链表中的对应节点
this.hashMap.set(key, curNode)
// 获取老的最新节点
const oldNode = this.headNode.next
// 更新节点指向
oldNode.pre = curNode
curNode.pre = this.headNode
this.headNode.next = curNode
curNode.next = oldNode
// 过量则删除尾节点对应数据
if (this.hashMap.size > this.capacity) {
this.hashMap.delete(this.tailNode.pre.key)
this.tailNode.pre = this.tailNode.pre.pre
this.tailNode.pre.next = this.tailNode
}
}
}
node (key: number, val: number) {
return {
pre: null,
next: null,
val,
key
}
}
}