最近最少使用缓存(LRU Cache)
2023-08-05· 15min
#LRU Cache
- LRU(Least Recently Used) 即: 最近最少使用。
- LRU Cache,一种缓存淘汰策略,在有限的内存空间内,只缓存最近使用的数据,超过有限内存空间的数据将会被删除。
#场景
- LRU,一个在产品业务中很有用的算法,常适用于缓存服务、部分业务场景、底层的内存管理等。
#代码实现
- 分析
- 哈希表: 用来映射每个键到链表中的节点
- 双向链表: 高效地在任意位置插入和删除节点
- 从头部添加节点到链表,从尾部删除节点(删除的同时从哈希表里面删除对应的key),再次访问的节点,需要把节点移动到链表的头部。
- 哨兵节点(dummy node),初始的prev、next指向自己。随节点插入,next指向链表第一个节点,prev指向链表最后一个节点。
- 时间复杂度: O(1)、空间复杂度: O(min(putCount, capacity))
#JS 实现
class Node {
constructor(key = 0, value = 0) {
this.key = key;
this.value = value;
this.prev = null;
this.next = null;
}
}
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.dummy = new Node();
this.dummy.prev = this.dummy;
this.dummy.next = this.dummy;
this.keyNodeMap = new Map();
}
moveToFront(node) {
node.prev = this.dummy;
node.next = this.dummy.next;
node.prev.next = node;
node.next.prev = node;
}
get(key) {
if (!this.keyNodeMap.has(key)) {
return null;
}
const node = this.keyNodeMap.get(key);
this.remove(node);
this.moveToFront(node);
return node;
}
remove(node) {
node.prev.next = node.next;
nodex.next.prev = node.prev;
}
put(key, value) {
let node = this.get(key);
if (node) {
// 存在 -> 更新value
node.value = value;
return;
}
node = new Node(); // 不存在 -> 新增
this.keyNodeMap.set(key, node);
this.moveToFront(node);
if (this.keyNodeMap.size > this.capacity) {
// 空间不足 -> 删除最后一个节点
const lastNode = this.dummy.prev;
this.keyNodeMap.delete(lastNode.key);
this.remove(lastNode);
}
}
}
#GO实现
package main
import (
"container/list"
"fmt"
)
type entry struct {
key, value int
}
type LRUCache struct {
capacity int
list *list.List
keyNodeMap map[int]*list.Element
}
func Constructor(capacity int) LRUCache {
return LRUCache{capacity, list.New(), map[int]*list.Element{}}
}
func (c *LRUCache) Get(key int) int {
node := c.keyNodeMap[key]
if node == nil {
return -1
}
c.list.MoveToFront(node)
return node.Value.(entry).value
}
func (c *LRUCache) Put(key, value int) {
if node := c.keyNodeMap[key]; node != nil { // 存在 -> 更新value
node.Value = entry{key, value}
c.list.MoveToFront(node)
return
}
c.keyNodeMap[key] = c.list.PushFront(entry{key, value}) // 不存在 -> 新增
if len(c.keyNodeMap) > c.capacity { // // 空间不足 -> 删除最后一个节点
delete(c.keyNodeMap, c.list.Remove(c.list.Back()).(entry).key)
}
}
func main() {
cache := Constructor(2)
cache.Put(1, 1)
cache.Put(2, 2)
fmt.Println(cache.Get(1)) // 1
cache.Put(3, 3) // put 3, remove 2
fmt.Println(cache.Get(2)) // -1
cache.Put(4, 4) // put 4, remove 2
fmt.Println(cache.Get(1)) // -1
fmt.Println(cache.Get(3)) // 3
fmt.Println(cache.Get(4)) // 4
}
#题解
#扩展
- 页面置换算法(Page-Replacement Algorithms)
- 最佳(OPT)
- 先进先出(FIFO)
- 最少使用(LFU)