什么是LRU?
缓存在计算机网络上随处可见,例如:当我们首次访问一个网页时,打开很慢,但当我们再次打开这个网页时,打开就很快。
这就涉及缓存在浏览器上的应用:浏览器缓存。当我们打开一个网页时,它会在发起真正的网络请求前,查询浏览器缓存,看是否有要请求的文件,如果有,浏览器将会拦截请求,返回缓存文件,并直接结束请求,不会再去服务器上下载。如果不存在,才会去服务器请求。
其实,浏览器中的缓存是一种在本地保存资源副本,它的大小是有限的,当我们请求数过多时,缓存空间会被用满,此时,继续进行网络请求就需要确定缓存中哪些数据被保留,哪些数据被移除,这就是浏览器缓存淘汰策略,最常见的淘汰策略有 FIFO(先进先出)、LFU(最少使用)、LRU(最近最少使用)。
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
题目
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。 实现 LRUCache 类:
- LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
- int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
- void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例
-
输入:["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get" [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
-
输出:[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
- LRUCache lRUCache = new LRUCache(2);
- lRUCache.put(1, 1); // 缓存是 {1=1}
- lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
- lRUCache.get(1); // 返回 1
- lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
- lRUCache.get(2); // 返回 -1 (未找到)
- lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
- lRUCache.get(1); // 返回 -1 (未找到)
- lRUCache.get(3); // 返回 3
- lRUCache.get(4); // 返回 4
AC代码
- 数组+对象实现方式
var LRUCache = function(capacity) {
this.keys = []
this.cache = Object.create(null)
this.capacity = capacity
};
LRUCache.prototype.get = function(key) {
if(this.cache[key]) {
// 调整位置
remove(this.keys, key)
this.keys.push(key)
return this.cache[key]
}
return -1
};
LRUCache.prototype.put = function(key, value) {
if(this.cache[key]) {
// 存在即更新
this.cache[key] = value
remove(this.keys, key)
this.keys.push(key)
} else {
// 不存在即加入
this.keys.push(key)
this.cache[key] = value
// 判断缓存是否已超过最大值
if(this.keys.length > this.capacity) {
removeCache(this.cache, this.keys, this.keys[0])
}
}
};
// 移除 key
function remove(arr, key) {
if (arr.length) {
const index = arr.indexOf(key)
if (index > -1) {
return arr.splice(index, 1)
}
}
}
// 移除缓存中 key
function removeCache(cache, keys, key) {
cache[key] = null
remove(keys, key)
}
- Map: 利用 Map 既能保存键值对,并且能够记住键的原始插入顺序
/**
* @param {number} capacity
*/
var LRUCache = function(capacity) {
this.cache = new Map()
this.capacity = capacity
};
/**
* @param {number} key
* @return {number}
*/
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
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
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)
};
题目链接:146. LRU 缓存机制
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情:juejin.cn/post/693314…