为了账号安全,请及时绑定邮箱和手机立即绑定

【九月打卡】第9天 手写 LRU缓存

标签:
JavaScript

课程名称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;
      }
    }

以上,结束!

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消