题目描述
分析
实现 LRU,为实现快速的查找,删除,我会用哈希表和双向链表来完成实现
这是一道很好的考察数据结构的题目,涉及两个数据结构的实现
算法
哈希表,双向链表
过程
实现数据结构(hashSet,DoubleLinkedList) -> 实现方法
数据结构
对于哈希表:
class Hash {
constructor() {
this.base = 697
this.data = new Array(this.base).fill(0).map(() => new Array())
}
set(key, value) {
const ind = this.hash(key)
const it = this.data[ind]
for (const x of it) {
if (x[0] === key) {
x[1] = value
return
}
}
it.push([key, value])
}
get(key) {
const ind = this.hash(key)
const it = this.data[ind]
for (const x of it) {
if (x[0] === key) return x[1]
}
return -1
}
delete(key) {
const ind = this.hash(key)
const it = this.data[ind]
for (let i = 0; i < it.length; i++) {
if (it[i][0] === key) {
it.splice(i, 1)
return
}
}
}
contains(key) {
const ind = this.hash(key)
const it = this.data[ind]
for (const x of it) {
if (x[0] === key) {
return true
}
}
return false
}
hash(key) {
return key % this.base
}
}
双向链表
class ListNode {
constructor(key, value) {
this.key = key
this.value = value
this.prev = null
this.next = null
}
}
实现 LRU 方法
实例的数据
头节点,尾节点
头节点:dummyHead, 尾节点:dummyTail
在缓存过程中,一定涉及到过期数据的清理与新数据的插入,因此一个 LRU 实例一定会保存头节点,尾节点
首先将他们声明为空节点: new ListNode,在链接起来,使成为环形链表
容量
this.capacity 作为容量
缓存总量
this.cnt 作为已经存储的数据的数量
哈希表
this.hash 作为存储数据的哈希
方法
LRUCache.prototype.get
作用是拿到某个 key 的元素
从哈希中拿出元素 node,此时如果数据不存在的话值是 -1
存在:移动节点至头部,返回 node.value
不存在:返回 -1
LRUCache.prototype.moveToHead
我加的方法,作用是将某个节点移动到头部,即 this.dummyHead.next
(此处再次说明 dummyHead, dummyTail 的优势,没必要考虑现在链表里有多少个节点,不用判空)
函数体内再次调用了我自定义的两个方法
removeFromList,删除 node -> addToHead,将 node 移动到头部
两方法均为改变节点指针指向,不再赘述
LRUCache.prototype.put
作用是设置某个 key 为 value
查找 key 对应的节点,从 hash 中找到对应节点,此时有两种情况
不存在:如果判断插入后超过了 capacity,需要先删除一个尾节点, 否则直接将新节点插入链表并在 hash 记录,更新 cnt,请注意我实现的 addToHead 方法
存在:更新对应节点的 value,并调用 moveToHead 更新缓存记录
代码
/**
* @param {number} capacity
*/
var LRUCache = function(capacity) {
this.capacity = capacity
this.hash = new Hash()
this.cnt = 0
this.dummyHead = new ListNode()
this.dummyTail = new ListNode()
this.dummyHead.next = this.dummyTail
this.dummyTail.next = this.dummyHead
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function(key) {
const node = this.hash.get(key)
if (node === -1) return -1
this.moveToHead(node)
return node.value
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function(key, value) {
const node = this.hash.get(key)
if (node === -1) {
if (this.capacity === this.cnt) this.removeLRUItem()
let newNode = new ListNode(key, value)
this.hash.set(key, newNode)
this.addToHead(newNode)
this.cnt++
} else {
node.value = value
this.moveToHead(node)
}
};
/**
* @param {ListNode} node
* @return {void}
*/
LRUCache.prototype.moveToHead = function(node) {
this.removeFromList(node)
this.addToHead(node)
};
/**
* @param {ListNode} node
* @return {void}
*/
LRUCache.prototype.removeFromList = function(node) {
const next = node.next
const prev = node.prev
prev.next = next
next.prev = prev
};
/**
* @param {ListNode} node
* @return {void}
*/
LRUCache.prototype.addToHead = function(node) { // 插入到虚拟头结点和真实头结点之间
node.prev = this.dummyHead // node的prev指针,指向虚拟头结点
node.next = this.dummyHead.next // node的next指针,指向原来的真实头结点
this.dummyHead.next.prev = node // 原来的真实头结点的prev,指向node
this.dummyHead.next = node // 虚拟头结点的next,指向node
}
/**
* @return {void}
*/
LRUCache.prototype.removeLRUItem = function() {
const tail = this.popTail()
this.hash.delete(tail.key)
this.cnt--
};
/**
* @return {void}
*/
LRUCache.prototype.popTail = function() {
const tail = this.dummyTail.prev
this.removeFromList(tail)
return tail
};
class ListNode {
constructor(key, value) {
this.key = key
this.value = value
this.prev = null
this.next = null
}
}
class Hash {
constructor() {
this.base = 697
this.data = new Array(this.base).fill(0).map(() => new Array())
}
set(key, value) {
const ind = this.hash(key)
const it = this.data[ind]
for (const x of it) {
if (x[0] === key) {
x[1] = value
return
}
}
it.push([key, value])
}
get(key) {
const ind = this.hash(key)
const it = this.data[ind]
for (const x of it) {
if (x[0] === key) return x[1]
}
return -1
}
delete(key) {
const ind = this.hash(key)
const it = this.data[ind]
for (let i = 0; i < it.length; i++) {
if (it[i][0] === key) {
it.splice(i, 1)
return
}
}
}
contains(key) {
const ind = this.hash(key)
const it = this.data[ind]
for (const x of it) {
if (x[0] === key) {
return true
}
}
return false
}
hash(key) {
return key % this.base
}
}