持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情
- Hello,这里是mouche,当然你也可以叫我某车,反正大家都爱这么叫😁
- 写这篇的原因是因为突然看到了
LRU,想到这不就是页面置换算法吗 - 同时又想起了在力扣做过实现LRU这道题146. LRU 缓存 - 力扣(LeetCode),于是想整理整理
- 那么就来动动手吧
一、什么是LRU
- 直接通过力扣附带的链接可以看到百度百科的解释(与《计算机网络操作系统(第四版)》的解释一致)
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当需淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰
- 简单一点就是说:诶小老弟,我不够地方放东西了,上次用你的时间离现在最久,只能跟你说拜拜喽
二、实现思路
以下都使用大白话
-
首先是要知道存放东西容器的容量
capacity -
实际上我们应该是采用特殊栈的思想
- 当使用该东西的时候就先判断栈中是否有它,
- 如果有的话把它从栈中移出,然后把它压入栈顶
- 如果没有的话则判断栈是否已经满了
- 满了的话就丢掉栈底的东西,再把我们使用的东西压入栈顶
- 没满的话就直接压入栈顶即可
- 这样就能保证栈顶始终保持为最新被使用的东西,且栈底为最近最久未使用的东西
-
来个流程图就很清晰了
-
因为
Map字典结构可以通过deleteAPI直接删除某个指定的键,所以我们使用Map来操作比直接使用数组方便,同时Map是顺序存储的,所以也木有什么问题,不过要注意的是,最近最久未使用的应该是在Map的第一组数据,而最新的则为最后一组数据,你可以理解为它的开口向下
(截图于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,走到底后done为true,无论再next多少次,value都为undefined
四、测试
- 来个例子
- 对代码进行测试
//我在每一步打印了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);
- 结果,跟上述图一致
五、完成力扣题目
-
多要求了一个API:
int get(int key)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1- 也许你会像我一样,看到就直接一句话解决
get(key) { return this.stack.has(key) ? this.stack.get(key) : -1; } -
但是你去找它的时候,它存在的时候即要去获取它的值,这个时候也是属于使用它了,emmm....怎么不算呢
-
所以应该也对它进行一个删除记录压入栈顶的操作
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;
}