LRU缓存机制

目录

 

LRU(Least Recently Used)最近少用

what

why

where

how

int get(int key)

void put(int key,int value)

数据结构的选用

具体实现(附详细注释)


LRU(Least Recently Used)最近少用

  • what

它是一种缓存淘汰机制,顾名思义,将最近使用次数最少的缓存淘汰


  • why

根据程序的局部性原理,最近经常使用的缓存留在内存中,而将最近不常用的缓存换出内存,是比较接近理想的一种缓存置换算法


  • where

在凡是带有缓存的场景中,LRU都可以作为最基本的缓存置换策略,例如:操作系统内存的页面置换、Redis缓存淘汰机制等等


  • how

如何设计一个简单的LRU缓存算法?

举个栗子,内存容量大小为2,按照key-value形式存储,结构的头部为最近最常使用的结点,尾部为最近最少使用的结点

LRU有两个操作:get和put,每次get和put操作都将查询的值变为最近最常使用的结点,当put容量已满时,删除最近最少使用的结点


int get(int key)

  1. 输入一个key值
  2. 如果key值不存在,则返回-1
  3. 如果key值存在,则将key-value键值对放到结构头部

void put(int key,int value)

  1. 首先查看是否存在相同key值的结点
  2. 如果存在,把它删除;
  3. 如果不存在,先查看容量是否已满
  4. 如果容量已经满了,从尾部删除最近最少使用的结点,为新插入结点腾出位置
  5. 如果容量未满,在最前面放入一个key-value的键值对

数据结构的选用

对于get操作的第2.3步

需要通过key值查找是否存在,则应该使得查找的时间复杂度为O(1),链式结构肯定不可以,因为需要遍历查找,考虑使用数组、hash

如果查找到key值对应的结点存在,需要将key-value键值对放到结构头部,则应该使得头部插入的时间复杂度为O(1),考虑使用链表

对于put操作的第1步

同get操作

对于put操作的第2步

则应该使得在任意位置删除的时间复杂度为O(1),数组结构肯定不可以,因为删除后需要向前移动补齐,考虑使用链式结构

对于put操作的第4步

则需要在尾部删除的时间复杂度为O(1),数组链表皆可

对于put操作的第5步

同get操作


综合考虑,因为需要在任意位置删除的时间复杂度为O(1),数组结构肯定是不能选用的,那么如果使用链式结构,该如何在O(1)时间找到这个要删除的位置呢?

思路是这样的,

我们需要以时间复杂度O(1)得知Cache中key值对应的结点是否存在,可以使用一个hash映射

我们需要在头插尾删时间复杂度为O(1),那么使用双向链表是最好的选择,想要在双向链表中以时间复杂度O(1)删除某个结点,则首先要找到这个结点对应的迭代器

那么这个hash映射,key值就是结点的key值,而value值,我们存放key值对应结点的迭代器,通过迭代器调用erase就可以实现以O(1)时间复杂度删除指定位置结点

它的结构图是下面酱婶儿的

 

使用这种方式我们可以实现以下操作的时间复杂度都是O(1):

在Cache的头插入、任意位置删除

查找key值对应的结点是否在Cache中


具体实现(附详细注释)

class LRUCache {
public:
    list<pair<int,int>> cache;
    unordered_map<int,list<pair<int,int>>::iterator> hash;
    int cap;
    LRUCache(int capacity) :cap(capacity){
    }
    int get(int key) {
        //先看映射中有没有 没有 返回-1
        if(hash.find(key) == hash.end())
            return -1;
        //如果有,取得结点
        pair<int,int> node = *hash[key];
        //通过结点的迭代器,删除
        cache.erase(hash[key]);
        //将结点重新插入到头部,变为最近最常使用
        cache.push_front(node);
        //更新hash中迭代器的位置
        hash[key] = cache.begin();
        //返回value值
        return node.second;
    }
    void put(int key, int value) {
        //先看映射中有没有
        if(hash.find(key) != hash.end()){
            //有 通过迭代器删除
            cache.erase(hash[key]);
        }else{
            //没有 判断这次插入是否越界
            if(cap == cache.size()){
                //越界了 
                //注意顺序:通过Cache取得key值,删除hash中的该结点
                //在Cache尾巴删除最近最少使用的结点
                hash.erase(cache.back().first);
                cache.pop_back();
            }
        }
        //头插入
        cache.push_front(make_pair(key,value));
        //更新map的迭代器
        hash[key] = cache.begin();
            
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值