LRU缓存

LRU缓存

其实LRU不难实现,但是要实现高效的LRU比较难,就是要实现增删查改均为O(1)

理解核心思想

想象LRU缓存就像一个只能放3本书的小书架

  • 最近看过的书要放在最左边
  • 当书架满了要加新书时,把最右边(最久没看)的书扔掉

数据结构的作用(非常简单!)

  • list<pair<int, int>> cache :就是那个小书架 ,按阅读顺序放书 list.front()= 最近看的(最左边) list.back()= 最久没看的(最右边)
  • unordered_map<int, ...> map :是书的索引卡片 通过书名(key)快速找到书在书架上的位置 值类型是list<pair<int, int>>::iterator= 书在书架上的位置指针

关键操作拆解(对应现实动作)

1. get操作:找书看

复制代码
int get(int key) {
    // 1. 先查索引卡:这本书在书架上吗?
    auto it = map.find(key);  // it是指向索引卡条目的指针
    if (it == map.end()) return -1; // 没这本书
    
    // 2. 找到书了!把这本书抽出来放到最左边
    // cache.splice(位置, 书架, 书的位置) = 把书从当前位置移到最前面
    cache.splice(cache.begin(), cache, it->second);
    
    // 3. 返回书的内容
    return it->second->second; // it->second是书的位置,->second是书的内容
}

2. put操作:放书

复制代码
void put(int key, int value) {
    // 情况1:书已存在(更新)
    auto it = map.find(key);
    if (it != map.end()) {
        it->second->second = value; // 更新书的内容
        cache.splice(cache.begin(), cache, it->second); // 移到最前面
        return;
    }
    
    // 情况2:新书,但书架满了
    if (cache.size() == cap) {
        // 扔掉最右边的书
        int key_to_del = cache.back().first; // 最旧书的书名
        map.erase(key_to_del);  // 从索引卡中撕掉这张卡片
        cache.pop_back();       // 把书从书架最右边扔掉
    }
    
    // 把新书放到最左边
    cache.push_front({key, value}); // 书架上放新书
    map[key] = cache.begin();      // 索引卡记录:书名->书的位置(最左边)
}

迭代器到底是什么?(超简单理解)

list::iterator就想象成书签手指

  • 指向链表中的某个具体位置
  • it->second就是通过书签拿到指向的那本书

完整模拟过程(cap=2)

复制代码
操作序列:put(1,1), put(2,2), get(1), put(3,3)

1. put(1,1): 书架=[(1,1)], 索引卡:1->指向(1,1)
2. put(2,2): 书架=[(2,2),(1,1)], 索引卡:1->指向(1,1), 2->指向(2,2)  
3. get(1): 找到书1,移到最前:书架=[(1,1),(2,2)]
4. put(3,3): 书架已满,扔掉最右边的(2,2)
   - 先更新索引卡:删除key=2
   - 书架变成[(1,1)],然后插入(3,3)到最前:[(3,3),(1,1)]

class LRUCache {
private:
    int cap;
    list<pair<int, int>> cache; // 书架:存放(key,value)对
    unordered_map<int, list<pair<int, int>>::iterator> map; // 索引卡:key->书的位置

public:
    LRUCache(int capacity) : cap(capacity) {}

    int get(int key) {
        // 在索引卡中找这本书
        auto map_it = map.find(key);
        if (map_it == map.end()) return -1; // 没找到
        
        // map_it->second 是书在书架上的位置(迭代器)
        // 把这本书移到书架最前面
        cache.splice(cache.begin(), cache, map_it->second);
        
        // 返回书的内容:map_it->second指向书,->second是value
        return map_it->second->second;
    }

    void put(int key, int value) {
        auto map_it = map.find(key);
        
        if (map_it != map.end()) {
            // 书已存在:更新内容并移到最前
            map_it->second->second = value; // 更新书的内容
            cache.splice(cache.begin(), cache, map_it->second);
            return;
        }
        
        // 新书,但书架满了
        if (cache.size() == cap) {
            // 扔掉最旧的书(书架最后面)
            pair<int, int> old_book = cache.back(); // 最旧的书
            map.erase(old_book.first);  // 从索引卡删除
            cache.pop_back();           // 从书架扔掉
        }
        
        // 插入新书到书架最前面
        cache.push_front({key, value});
        // 更新索引卡:记录新书的位置(书架最前面)
        map[key] = cache.begin();
    }
};

关键记住

  • list是物理书架,存实际数据
  • unordered_map是索引卡,只存位置指针
  • 所有操作都要同时更新书架和索引卡
相关推荐
quantdash_cc1 小时前
历史数据断层与REST请求慢到崩溃?QuantDash高性能量化数据API终极解决方案
开发语言·python·缓存·php·量化·quantdash
一次旅行2 小时前
2026‑08‑22 AI产业深度解读|Anthropic自研芯片布局、SGLang权重缓存守护进程、Agent任务作弊审计、AI原生SDLC
人工智能·缓存·sglang
晚安code2 小时前
Redis 内存淘汰策略实战:什么时候淘汰、怎么选、怎么防缓存击穿
java·redis·缓存
CDN36013 小时前
CDN 缓存策略优化实操:出海业务提升缓存命中率,TTL、缓存 Key、刷新配置与踩坑总结
缓存·cdn加速·cdn防护·360cdn
mit6.82419 小时前
保持缓存的有效性
缓存
AI多Agent协作实战派1 天前
AI多Agent协作系统实战(四十六):改了十次规则,AI员工还是老样子——会话缓存的坑
java·spring·缓存
做一个AK梦2 天前
Redis 深入浅出
java·数据库·redis·缓存
Java牛马2 天前
Caffeine 缓存及其应用相关总结
java·redis·缓存·caffeine·数据一致性·本地缓存
xiaojiaohuazi2 天前
国产安陆EG4S20 FPGA实现千兆以太网TCP/IP:AD7606C 8通道1 MSPS采集、SDRAM缓存与Python上位机
tcp/ip·缓存·fpga开发
Nil2082 天前
leetcode 146LRU缓存
算法·leetcode·缓存