146. LRU Cache
题目简介
本题是一道系统设计的题目,要求我们实现一个最近最少使用缓存 LRU
LRU 的初始化传入一个参数 capacity,声明此缓存的容量
LRU 支持两个方法: put 与 get
其中 put 支持插入/更新一个数据,这个数据会被标记为最新被使用数据,如果插入的数据超过了缓存数量,最久没有被使用的数据会被删除
get 支持读取当前缓存的特定值,被读取的会被标记为最新被使用数据,如果读取的数据不在缓存中则返回 -1
解题思路
由于题目要求我们 get 与 put 都需要 O(1) 的复杂度,所以我们可以用链表配合 map 来做
链表负责维护当前数据的新旧、map 用来判断当前数据是否存在,以及读取数据的值
具体而言,当一个数据越靠近链表头,我们认为它越久没有被使用;当一个数据越靠近链表尾,我们认为它上次被使用时间离现在越近
对于 get 方法来说有两种可能:
- get 的数据不存在缓存中:此时我们无需操作链表
- get 的数据在缓存中:我们需要把链表对应到该数据的元素移动到链表尾
对于 put 方法来说,也有两种可能:
- 当前数据在缓存中,需要更新:此时与 get 的数据在缓存中的场景一致,需要把链表对应到该数据的元素移动到链表尾
- 当前数据不在缓存中,需要插入:此时我们需要创建一个链表元素,并放置在链表尾;需要注意的是,如果此时缓存容量超了,需要从链表头删除一个数据
总结一下,我们需要对链表进行两种操作:
- 删除链表中任意元素
- 添加元素到链表尾
为了让这两个操作复杂度最低,我们需要双向链表 Double-linked list
Javascript
class DLN {
constructor(key, value) {
this.key = key
this.value = value
this.prev = null
this.next = null
}
}
/**
* @param {number} capacity
*/
var LRUCache = function (capacity) {
this.capacity = capacity
this.map = new Map()
// head.next points to the least recent used node(the most possible removed node)
this.head = new DLN()
// tail.prev points to the last recent used node
this.tail = new DLN()
// we use 2 empty nodes to cover all nodes, so we can modify the link easily
this.head.next = this.tail
this.tail.prev = this.head
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function (key) {
if (!this.map.has(key)) {
return -1
}
const node = this.map.get(key)
this.remove(node)
this.add(node)
return node.value
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function (key, value) {
if (this.get(key) !== -1) {
this.tail.prev.value = value
return
}
if (this.map.size === this.capacity) {
this.map.delete(this.head.next.key)
this.remove(this.head.next)
}
const node = new DLN(key, value)
this.map.set(key, node)
this.add(node)
};
// add the node too the tail of the link
LRUCache.prototype.add = function (node) {
const prevNode = this.tail.prev
const nextNode = this.tail
prevNode.next = node
node.prev = prevNode
node.next = this.tail
this.tail.prev = node
}
// remove the node from the link
LRUCache.prototype.remove = function (node) {
node.next.prev = node.prev
node.prev.next = node.next
}
/**
* 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)
*/