LRU缓存

一、概念

LRU缓存(Least Recently Used Cache)是一种常用的缓存淘汰算法,它基于"最近最少使用"的原则来管理缓存中的数据对象。当缓存空间不足时,LRU缓存会淘汰最久未被使用的数据,以确保缓存中始终存储着最新和最频繁使用的数据。

二、基本原理

LRU缓存的核心思想是:最近被访问的数据在未来可能会被再次访问,因此应该保留最近被访问的数据,淘汰最久未使用的数据。具体来说,LRU通过以下步骤工作:

  1. 缓存访问:当一个数据被访问时,算法会记录这个数据的访问时间或更新其访问顺序。
  2. 缓存插入:当需要将新数据插入缓存时,如果缓存尚未满,则直接插入。如果缓存已满,则使用LRU策略淘汰最久未使用的数据。
  3. 缓存淘汰:依据LRU策略,淘汰那些最久未被使用的数据,即那些距离上次访问时间最长的缓存项。

三、双向链表+哈希表实现

链表结点:

存储key的原因是缓存淘汰时可以获取到淘汰结点的key,从而删除哈希表中对应的值。

使用双向链表的原因是单向链表无法获取前一个元素,无法只通过结点指针删除该结点。

cpp 复制代码
struct mListNode
{
    int key;
    int value;
    struct mListNode *next;
    struct mListNode *pre;
    mListNode():key(0),value(0),next(nullptr),pre(nullptr){}
    mListNode(int k,int v):key(k),value(v),next(nullptr),pre(nullptr){}
};

双向链表:

在双向链表的实现中,使用一个伪头部(dummy head)和伪尾部(dummy tail)标记界限,这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在。

缓存访问:moveToTail

缓存插入:addToTail,再判断是否超出capacity,若超过则缓存淘汰

缓存淘汰:removeOldest从表头移除元素

cpp 复制代码
class DLinkedList
{
public:
    DLinkedList()
        :_head(new mListNode)
        ,_tail(new mListNode)
        ,_size(0)
    {
        _head->next=_tail;
        _tail->pre=_head;
    }
    mListNode *addToTail(int key,int value)
    {
        mListNode *pNew=new mListNode(key,value);
        pNew->next=_tail;
        pNew->pre=_tail->pre;
        _tail->pre->next=pNew;
        _tail->pre=pNew;
        ++_size;
        return pNew;
    }
    void removeElem(mListNode *p)
    {
        p->pre->next=p->next;
        p->next->pre=p->pre;
        delete p;
        p=nullptr;
        --_size;
    }
    int removeOldest()
    {
        int key=_head->next->key;
        removeElem(_head->next);
        return key;
    }
    void show()//调试用
    {
        if(_size==0) return;
        mListNode *cur=_head->next;
        while(cur->next)
        {
            cout<<cur->key<<":"<<cur->value<<" ";
            cur=cur->next;
        }
        cout<<"\n";
    }
    void moveToTail(mListNode *p)
    {
        p->pre->next=p->next;
        p->next->pre=p->pre;
        p->next=_tail;
        p->pre=_tail->pre;
        _tail->pre->next=p;
        _tail->pre=p;
    }
    int size() const
    {
        return _size;
    }
private:
    mListNode *_head;
    mListNode *_tail;
    int _size;
};

LRU缓存:

cpp 复制代码
class LRUCache {
public:
    unordered_map<int,mListNode*> cache;
    int cap;
    DLinkedList *list;
public:
    LRUCache(int capacity)
    :cap(capacity)
    ,list(new DLinkedList())
    {
    }

    int get(int key) {
        if(!cache.count(key))
        {
            return -1;
        }
        mListNode *p=cache[key];
        list->moveToTail(p);
        return p->value;
    }
    void put(int key, int value) {
        if(cache.count(key))
        {
            mListNode *p=cache[key];
            p->value=value;
            list->moveToTail(p);
        }
        else
        {
            mListNode *p=list->addToTail(key,value);
            cache[key]=p;
        }
        while(cap<list->size())
        {
            int key=list->removeOldest();
            cache.erase(key);
        }
    }
};

四、使用std::list+std::unordered_map实现

cpp 复制代码
class LRUCache {
public:
    unordered_map<int,std::list<pair<int,int>>::iterator> cache;
    int cap;
    list<pair<int,int>> list;
public:
    LRUCache(int capacity)
    :cap(capacity)
    ,list()
    {
    }

    int get(int key) {
        if(!cache.count(key))
        {
            return -1;
        }
        auto it=cache[key];
        list.splice(list.begin(),list,it);
        return it->second;
    }
    void put(int key, int value) {
        if(cache.count(key))
        {
            auto it=cache[key];
            it->second=value;
            list.splice(list.begin(),list,it);
            cache[key]=list.begin();
        }
        else
        {
            list.push_front(make_pair(key,value));
            cache[key]=list.begin();
        }
        while(cap<list.size())
        {
            auto it=list.back();
            list.pop_back();
            cache.erase(it.first);
        }
    }
};
相关推荐
进击的_鹏15 小时前
从零开始的 Redis 学习
服务器·数据库·c++·redis·缓存
摇曳的精灵17 小时前
前端缓存优化
前端·缓存·缓存优化
见山是山-见水是水20 小时前
拆解网络缓存策略设计:原生鸿蒙页面的实现路径与调试方法
http·缓存·华为·harmonyos
T011561821 小时前
艺培场馆课时预约小程序用户端 & 后台联动踩坑上篇(预约、课时、缓存、消息、学员端自身问题)
java·缓存·小程序·bug·全栈项目实战手记·艺培课时系统‘’
北漂燕郊杨哥1 天前
npm 缓存占满 C 盘?三步把它迁到 D 盘(附验证与避坑)
缓存·npm·node
克里斯蒂亚诺更新1 天前
win下如何将redis自启动
数据库·redis·缓存
晓晓_za8986682 天前
Geo 优化服务 CI/CD 流水线搭建:源码自动构建、测试与灰度发布
运维·服务器·tcp/ip·spring·缓存·ci/cd
nvd112 天前
LiteLLM 与 Redis 响应缓存机制:从精确哈希匹配到模型底层 KV-Cache 边界
redis·缓存·哈希算法
名字还没想好☜2 天前
Next.js 用 cookies()/headers() 读请求信息:动态渲染触发、缓存失效与在 Server Action 里读写 cookie
前端·javascript·缓存·react·next.js·app router