缓存置换算法LRU

一、缓存置换算法介绍

在面试阿里前端的时候,面试官提出LRU(Least recently used,最近最少使用)的问题,开始的时候犯一些方向性的错误,在面试官的提示下才想出思路。

解决一个问题得分步骤,第一步也是最重要的一步,先搞懂LRU是什么,什么场景下使用LRU?

LRU是什么?

LRU是内存管理的一种页面置换算法,选择最近最久未使用的页面予以淘汰。

通俗一点,内存最多只能存放这么多数据,但是又有新的数据来了,就得从内存里移除一个,给新数据腾出空间。但是删除哪个数据呢?不能随便删一个吧,所以就得制定一个规矩。常见的算法就有LRU、FIFO、LFU。

LRU(Least recently used,最近最少使用)就是把那个最久没有使用过的数据移除。

FIFO(First in first out, 先进先出)就是最先存入的数据最先淘汰。

LFU(Least Frequently Used,最近最少使用)就是最近使用频率最低的被淘汰。

二、算法思路

算法思路如下:

  1. 判断缓存是否存在,是则转5,否则转2
  2. 缓存空间是否已满,是则转3,否则转4
  3. 释放内存
  4. 存入缓存,转5
  5. 调用缓存,结束

上述过程如下图:

三、LRU算法

LRU场景如下

前端缓存假设只能存100个值,当101值存入的时候,就得删除原先的一个值,然后放入一个新的值。LRU的思想就是,要把最近最少使用的那个值移除。

问题来了,如何找到哪个值是最近最少使用的?

误区1: 给每个缓存都添加一个时间戳,操作了这个缓存之后更新时间戳,就能找到最近最少使用的缓存了。

误区2: 使用Map数据结构存放数据,将时间戳作为键,缓存作为值

上面两个误区就是我面试的时候踩的严严实实的两个问题,然后大佬帮我分析了一下。

最近最少使用是不是意味着,被调用或者刚存入的缓存就是最新的,然后仔细分析一下存入缓存这个过程:

  1. 最开始缓存为空,直接存入第一个缓存
  2. 存入第二个缓存的时候,奇迹发生了,无论是放在第一个前面还是后面,这两个都是有序的。
  3. 存入第三个时候只要按照前两个的顺序存放,就会保持一个顺序延续下去。这种数据结构像啥——链表呀。
  4. 根据第二步操作依次添加第三、第四和第五个缓存值,那缓存就是一个链表了,假设表头到表尾就是最新到最久的顺序

考虑一下读取缓存的情况:

  1. 按顺序遍历缓存,命中缓存时执行第二步,未命中时返回null
  2. 移除缓存,然后将缓存添加到链表头部

示意图和js实现代码如下:

LRU算法示意图

function Cache(value, key) {
  this.value = value;
  this.key = key;
}

function LRU() {
  this.localCache = [];
  this.maxLength = 5;
}

LRU.prototype.getCache = function(key) {
  for (var i = 0;i < this.localCache.length; i++) {
    if (this.localCache[i].key === key) {
      var temp = this.localCache.splice(i, 1);
      this.localCache.unshift(temp[0]);
      return temp;
    }
  }

  return null;
}

LRU.prototype.setCache = function(cache) {
  if (this.localCache.length === this.maxLength) {
    this.localCache.pop();
  }

  this.localCache.unshift(cache);
}


var c1 = new Cache('1', {p: 111});
var c2 = new Cache('2', {p: 222});
var c3 = new Cache('3', {p: 333});
var c4 = new Cache('4', {p: 444});
var c5 = new Cache('5', {p: 555});
var c6 = new Cache('6', {p: 666});

var lru = new LRU();

lru.setCache(c1);
lru.setCache(c2);
lru.setCache(c3);
lru.setCache(c4);
lru.setCache(c5);

console.log(lru);

lru.getCache('3');
console.log(lru);

lru.setCache(c6);
console.log(lru);

四、总结

这里只是对LRU进行简单的实现,很多地方都可以进行扩展的。比如说,将键值与下标放入map,查询效率直接从O(n)变成O(1)。还可以不使用js自带的Array对象实现,而是将每个数据节点变成一个带有前指针和后指针的node。

  • LRU算法其实挺简单的,不要走入上面提到的两个误区,这个问题很容易就能分析出来。
  • FIFO这个就更简单了,淘汰的时候从一个方向淘汰,被调用的也不需要额外处理。
  • LFU算法就比上面两个复杂一点,需要多保存一个数据(缓存使用的次数)。

下篇博客,将会补上另外两种的算法的js实现。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值