【九月打卡】第9天 手写 LRU缓存
课程名称:2周刷完100道前端优质面试真题
课程章节:第8章 前端面试技能拼图6: 编写高质量代码 - 正确,完整,清晰,鲁棒
主讲老师:双越
课程内容:
今天学习的内容包括:
8-19 用JS实现一个LRU缓存-分析数据结构特点,使用Map
8-20 用JS实现一个LRU缓存-代码演示和单元测试
8-21 【连环问】不用Map实现LRU缓存-分析问题,使用双向链表
8-22 【连环问】不用Map实现LRU缓存-代码演示
主要讲了 LRU (last recently used)缓存,以及用 map 或者 双向链表 + 对象实现 LRU 缓存。
课程收获
大致复述一下所学内容。
LRU 主要是在限定额度内,缓存数据,并把最后一次查询或设置数据设置为最近使用。当超出额度不够用时,把最末尾不经常使用的数据删除。
map 实现 LRU
map 存取无需遍历,get、set 都是 O(1)。
set 也可以 get, set 但是不方便再次排序。
思路:
初始化入参长度,new map。
get 直接借助 map.get(k); 然后移到最新位置。
set map.has(k) 看是否要删除之前的,然后 map.set(k, v)。如果长度超过,去掉最旧一位。
class LRUCache {
constructor(len) {
if (len < 1) throw new Error('invalid length');
this.len = len;
this.m = new Map();
}
set(k, v) {
const m = this.m;
if (m.has(k)) {
m.delete(k);
}
m.set(k, v);
if (m.length > this.len) {
const lastK = m.keys().next().value;
m.delete(lastK);
}
}
get(k) {
const m = this.m;
let v = null;
if (m.has(k)) {
v = m.get(k);
m.delete(k);
m.set(k, v);
}
return v;
}
}
双向链表 + 对象 实现 LRU
双向链表 有顺序,能排序,且能快速 set。 get 操作放对象上。
思路:
get:依据对象 data 取值,并放到链表末尾。
set:依据对象 data 查有无对应 key。
若之前有节点,修改值后放链表末尾。
若之前无节点,创建新节点设置key, value; 并把创建新节点放链表末尾,同步修改链表长度。若链表超长,则删除头节点。若链表只有一位,则把头节点设置到该节点。对象加入中也记录入创建新节点。
class LRUCache {
constructor(len) {
if (len < 1) throw new Error('invalid length');
this.maxLen = len;
// 对象 {key: node}
this.data = {};
// 链表相关
this.head = null;
this.tail = null;
this.datalen = 0;
}
get(k) {
let getNode = this?.data?.[k];
let v = getNode?.value || null;
if (getNode && (this.tail !== getNode)) {
this.move2Tail(getNode);
}
return v;
}
set(k, v) {
let getNode = this?.data?.[k];
if (getNode) {
getNode.value = v;
this.move2Tail(getNode);
} else {
// 新增
const newNode = {
key: k,
value: v
};
// 新增节点 对象
this.data[k] = newNode;
// 新增节点 链表
this.datalen++;
this.move2Tail(newNode);
if (this.datalen === 1) {
this.head = newNode;
}
if (this.datalen > this.maxLen) {
this.delOld();
}
}
}
move2Tail(node) {
if (node == this.tail) {
return ;
}
const prevN = node.prev || null;
const nextN = node.next || null;
if (prevN) {
prevN.next = nextN;
}
if (nextN) {
nextN.prev = prevN;
// 后移时,注意更改 head 指向
if (this.head === node) {
this.head = nextN;
}
}
const tail = this.tail || null;
if (tail) {
tail.next = node;
}
node.prev = tail;
node.next = null;
this.tail = node;
}
delOld() {
const head = this.head;
if (head == null) {
throw new Error('head is null')
}
const headNext = this.head.next;
if (headNext == null) {
throw new Error('head.next is null')
}
// 删除 obj k
delete this.data[head.key];
this.datalen--;
// 删除 链表头对应节点
headNext.prev = null;
head.next = null;
// head 指向下一个节点
this.head = headNext;
}
}
以上,结束!
共同学习,写下你的评论
评论加载中...
作者其他优质文章