LRU 是 Least Recently Used 的缩写,即最近最少使用,是一种常用的页面置换算法,选择内存中最近最久未使用的页面予以淘汰。

可用的 NodeJS 库见node-lru-cache

然怎么使用 JS 简单写一个?类似的题目见 LeetCode 146 LRU 缓存机制,进阶要求时间复杂度 O(1) 。

思路

解法:维护一个数组,提供 get 和 put 方法,并且限定 max 数量。

使用时,get 可以标记某个元素是最新使用的,提升它去第一项。put 可以加入某个key-value,但需要判断是否已经到最大限制 max

  • 若未到能直接往数组第一项里插入
  • 若到了最大限制 max,则需要淘汰数据尾端一个元素。
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得密钥 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得密钥 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4

具体代码

一般的解法,通过维护一个数组,数组项存放了 key-value 键值对对象,每次需要遍历去寻找 key 值所在的数组下标操作。

已经通过 leetCode 146 的检测。执行用时 : 720 ms。内存消耗 : 58.5 MB。

var LRUCache = function (capacity) {
this.capacity = capacity;
this.cache = [];
}; /**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function (key) {
let index = this.cache.findIndex((item) => item.key === key);
if (index === -1) {
return -1;
}
// 删除此元素后插入到数组第一项
let value = this.cache[index].value;
this.cache.splice(index, 1);
this.cache.unshift({
key,
value,
});
return value;
}; /**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function (key, value) {
let index = this.cache.findIndex((item) => item.key === key);
// 想要插入的数据已经存在了,那么直接提升它就可以
if (index > -1) {
this.cache.splice(index, 1);
} else if (this.cache.length >= this.capacity) {
// 若已经到达最大限制,先淘汰一个最久没有使用的
this.cache.pop();
}
this.cache.unshift({ key, value });
};

上面的做法其实有变种,可以通过一个对象来存键值对,一个数组来存放键的顺序。

来看进阶要求

时间复杂度 O(1),那就不能数组遍历去查找 key 值。可以用 ES6 的 Map 来解了,因为 Map 既能保持键值对,还能记住插入顺序。

var LRUCache = function (capacity) {
this.cache = new Map();
this.capacity = capacity;
}; LRUCache.prototype.get = function (key) {
if (this.cache.has(key)) {
// 存在即更新
let temp = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, temp);
return temp;
}
return -1;
}; LRUCache.prototype.put = function (key, value) {
if (this.cache.has(key)) {
// 存在即更新(删除后加入)
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 不存在即加入
// 缓存超过最大值,则移除最近没有使用的
this.cache.delete(this.cache.keys().next().value);
}
this.cache.set(key, value);
};

上述代码来自LRU 缓存机制-官方,执行用时 : 228 ms,内存消耗 : 59 MB

使用案例

JS 实现一个 LRU 算法的更多相关文章

  1. 面试题目:手写一个LRU算法实现

    一.常见的内存淘汰算法 FIFO  先进先出 在这种淘汰算法中,先进⼊缓存的会先被淘汰 命中率很低 LRU Least recently used,最近最少使⽤get 根据数据的历史访问记录来进⾏淘汰 ...

  2. 写一个LRU算法的记录

    今天简单记录一下,利用Scala解答的一道LRU题目,原题为LeetCode的第146题,是一道设计LRU的题目. 题目详情 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机 ...

  3. GuavaCache学习笔记一:自定义LRU算法的缓存实现

    前言 今天在看GuavaCache缓存相关的源码,这里想到先自己手动实现一个LRU算法.于是乎便想到LinkedHashMap和LinkedList+HashMap, 这里仅仅是作为简单的复习一下. ...

  4. LRU算法原理解析

    LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的. 现代操作系统提供了一种对主存的抽象概念虚拟内存,来对主存进行更好地管理.他将主存 ...

  5. 搞定redis面试--Redis的过期策略?手写一个LRU?

    1 面试题 Redis的过期策略都有哪些?内存淘汰机制都有哪些?手写一下LRU代码实现? 2 考点分析 1)我往redis里写的数据怎么没了? 我们生产环境的redis怎么经常会丢掉一些数据?写进去了 ...

  6. Guava---缓存之LRU算法

    随笔 - 169  文章 - 0  评论 - 292 GuavaCache学习笔记一:自定义LRU算法的缓存实现   前言 今天在看GuavaCache缓存相关的源码,这里想到先自己手动实现一个LRU ...

  7. 一个JS的日期格式化算法示例

    一个JS的日期格式化算法. 例子: <script> /** * Js日期格式化算法实例 * by www.jbxue.com */ function dateFormat(date, f ...

  8. 如何用 js 实现一个类似微信红包的随机算法

    如何用 js 实现一个类似微信红包的随机算法 js, 微信红包, 随机算法 "use strict"; /** * * @author xgqfrms * @license MIT ...

  9. LRU算法与LRUCache

    关于LRU LRU(Least recently used,最近最少使用)算法是操作系统中一种经典的页面置换算法,当发生缺页中断时,需要将内存的一个或几个页面置换出,LRU指出应该将内存最近最少使用的 ...

随机推荐

  1. NOIP模拟测试20「周·任·飞」

    liu_runda出的题再次$\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%$ 任 题解 题目中为什么反复强调简单路径,没有环 没有环的图中点数-边数=联通块数 前缀和维护边 ...

  2. 远程连接MySQL错误“plugin caching_sha2_password could not be loaded”的解决办法

    远程连接MySQL错误"plugin caching_sha2_password could not be loaded"的解决办法 问题描述: 今天在阿里云租了一个服务器,当我用 ...

  3. AS打包签名

    1.进入项目,然后点击菜单栏的Build  -->Generate  Signed APK... (如下图所示) 2.点击之后会出现下图,我这个是我以前有过KEY了,如果你以前没有过的话,都是空 ...

  4. c#在类中使用session

    先要继承页面的System.Web.UI.Page using System; using System.Collections.Generic; using System.Linq; using S ...

  5. 聊聊IOC中依赖注入那些事 (Dependency inject)

    What is Dependency injection 依赖注入定义为组件之间依赖关系由容器在运行期决定,形象的说即由容器动态的将某个依赖关系注入到组件之中在面向对象编程中,我们经常处理的问题就是解 ...

  6. Vue实现点击复制文本内容(原生JS实现)

    需求: 实现点击订单编号复制内容 实现步骤: 这里我是在element 的table组件里实现的步骤,仅供参考,实际上实现思路都大同小异 首先在需要点击的地方,添加点击事件 <div class ...

  7. Spring:Spring-IOC容器、DI依赖注入简介

    Spring容器到底是什么? 从概念上讲:Spring 容器是 Spring 框架的核心,是用来管理对象的.容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁. 从具象化 ...

  8. 矩阵按对角线打印---python

    将一个矩阵(二维数组)按对角线向右进行打印.(搜了一下发现好像是美团某次面试要求半小时手撕的题)Example:Input:[[1,2,3,4],[5,1,2,3],[9,5,1,2]]Output: ...

  9. 为什么使用 LSTM 训练速度远大于 SimpleRNN?

    今天试验 TensorFlow 2.x , Keras 的 SimpleRNN 和 LSTM,发现同样的输入.同样的超参数设置.同样的参数规模,LSTM 的训练时长竟然远少于 SimpleRNN. 模 ...

  10. cut和grep 选取命令

    cut命令 cut:将一段信息的某一段"切"出来,处理的信息是以行为单位.参数: -d :后接分隔字符,与-f一起使用: -f :依据-d的分隔字符将一段信息切割成为数段,用-f取 ...