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);
        }
    }
};
相关推荐
一直都在5729 小时前
Redis (一)
数据库·redis·缓存
秦jh_9 小时前
【Redis】客户端使用
数据库·redis·缓存
随风,奔跑11 小时前
Redis
数据库·redis·缓存
TlYf NTLE12 小时前
redis分页查询
数据库·redis·缓存
大萌神Nagato12 小时前
力扣HOT100 Q146LRU缓存
算法·leetcode·缓存
鬼蛟13 小时前
Redis
数据库·redis·缓存
8Qi814 小时前
Redis哨兵模式(Sentinel)深度解析
java·数据库·redis·分布式·缓存·sentinel
CDN36015 小时前
CDN 缓存命中率低如何提升?忽略参数与规则设置教程
运维·缓存
M--Y16 小时前
初识Redis
数据库·redis·缓存
皙然17 小时前
Redis核心理论:数据删除与淘汰策略详解(从原理到实战)
数据库·redis·缓存