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);
        }
    }
};
相关推荐
好心的小明7 小时前
【王树森推荐系统】召回11:地理位置召回、作者召回、缓存召回
人工智能·缓存·推荐系统·推荐算法
草履虫建模16 小时前
Redis:高性能内存数据库与缓存利器
java·数据库·spring boot·redis·分布式·mysql·缓存
程序猿ZhangSir18 小时前
Redis 缓存进阶篇,缓存真实数据和缓存文件指针最佳实现?如何选择?
数据库·redis·缓存
段帅龙呀1 天前
Redis构建缓存服务器
服务器·redis·缓存
夜斗小神社2 天前
【黑马点评】(二)缓存
缓存
Hello.Reader2 天前
Redis 延迟监控深度指南
数据库·redis·缓存
Hello.Reader2 天前
Redis 延迟排查与优化全攻略
数据库·redis·缓存
在肯德基吃麻辣烫3 天前
《Redis》缓存与分布式锁
redis·分布式·缓存
先睡3 天前
Redis的缓存击穿和缓存雪崩
redis·spring·缓存
CodeWithMe4 天前
【Note】《深入理解Linux内核》 Chapter 15 :深入理解 Linux 页缓存
linux·spring·缓存