如何用JS实现LRU

118 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

  • Hello,这里是mouche,当然你也可以叫我某车,反正大家都爱这么叫😁
  • 写这篇的原因是因为突然看到了LRU,想到这不就是页面置换算法吗
  • 同时又想起了在力扣做过实现LRU这道题146. LRU 缓存 - 力扣(LeetCode),于是想整理整理
  • 那么就来动动手吧

一、什么是LRU

  • 直接通过力扣附带的链接可以看到百度百科的解释(与《计算机网络操作系统(第四版)》的解释一致)

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当需淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰

  • 简单一点就是说:诶小老弟,我不够地方放东西了,上次用你的时间离现在最久,只能跟你说拜拜喽 04A1EEE0.gif

二、实现思路

以下都使用大白话

  • 首先是要知道存放东西容器的容量capacity

  • 实际上我们应该是采用特殊栈的思想

    • 使用该东西的时候就先判断栈中是否有它,
    • 如果有的话把它从栈中移出,然后把它压入栈顶
    • 如果没有的话则判断栈是否已经满
    • 满了的话就丢掉栈底的东西,再把我们使用的东西压入栈顶
    • 没满的话就直接压入栈顶即可
    • 这样就能保证栈顶始终保持为最新被使用的东西,且栈底为最近最久未使用的东西
  • 来个流程图就很清晰了 image.png

  • 因为Map字典结构可以通过deleteAPI直接删除某个指定的键,所以我们使用Map来操作比直接使用数组方便,同时Map是顺序存储的,所以也木有什么问题,不过要注意的是,最近最久未使用的应该是在Map的第一组数据,而最新的则为最后一组数据,你可以理解为它的开口向下

image.png (截图于MDN)

三、实现过程

  • 实现先确定应该是个类,先搭建最基本的结构
class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;  //确定容量
    this.stack = new Map(); //使用特殊栈
  }
  //使用某个东西
  put(key,value) {}
}
  • 实现put,其实按照上面的流程图,没啥好讲了已经哈哈
  //使用某个东西
  put(key , value) {
    if(this.stack.has(key)) {
      this.stack.delete(key) //有记录则删除
    }else if(this.stack.size == this.capacity) {
      this.stack.delete(this.stack.keys().next().value)  //满了则删栈底
    }
    this.stack.set(key, value);  //无论是走哪一个分支,最后都要把它放入栈顶
  }
  • 这里的next方法可以讲讲,因为Map是实现迭代协议,是可以迭代的
  • 所以使用keys().next().value则刚好为它的第一组数据
  • 只要它还没走到底,done就为false,走到底后donetrue,无论再next多少次,value都为undefined image.png

四、测试

  • 来个例子 image.png
  • 对代码进行测试
//我在每一步打印了map结构, 每一步放入的键为上面的数字,值为第几个加入的
const myCache = new LRUCache(3);
myCache.put(4,1);
myCache.put(1,2);
myCache.put(4,3);
myCache.put(3,4);
myCache.put(7,5);
  • 结果,跟上述图一致 image.png

五、完成力扣题目

  • 多要求了一个API:

    • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
    • 也许你会像我一样,看到就直接一句话解决
      get(key) {
        return this.stack.has(key) ? this.stack.get(key) : -1;
      }
    
  • 但是你去找它的时候,它存在的时候即要去获取它的值,这个时候也是属于使用它了,emmm....怎么不算呢

    04A2F275.jpg

  • 所以应该也对它进行一个删除记录压入栈顶的操作

  get(key) {
    if(!this.stack.has(key)) return -1;
    let value = this.stack.get(key);
    this.stack.delete(key);
    this.stack.set(key, value);
    return value;
  }