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);
        }
    }
};
相关推荐
zym大哥大9 小时前
C++客服端访问redis
数据库·redis·缓存
赖small强9 小时前
Linux内存管理-缓存系统中的Major和Minor详解
linux·缓存·交换缓存机制·major fault·minor fault
啊森要自信10 小时前
【GUI自动化测试】YAML 配置文件应用:从语法解析到 Python 读写
android·python·缓存·pytest·pip·dash
小高Baby@11 小时前
Redis Key的设计
数据库·redis·缓存
hzk的学习笔记14 小时前
Redis除了做缓存还能用来干什么
数据库·redis·缓存
小杨互联网14 小时前
构建推理缓存以节省高流量 LLM 应用程序的成本
缓存·llm·大型语言模型
xujiangyan_1 天前
Redis详解
数据库·redis·缓存
程序员小凯1 天前
Spring Boot缓存机制详解
spring boot·后端·缓存
夜泉_ly1 天前
Redis -持久化
数据库·redis·缓存
235161 天前
【LeetCode】146. LRU 缓存
java·后端·算法·leetcode·链表·缓存·职场和发展