LRU Cache 的原理及简单实现

LRU(Least Recently Used)是一种缓存淘汰算法,当未命中时,将最近最少使用(即上一次使用的时间离现在最久)的块置换出去来加载新块。

例如,以下块的访问顺序为 A B C D E D F:

一旦 A B C D 被放置在缓存中(每个新访问增量为 1),而后又访问 E 没有命中,需要将其替换进其中一个块中。根据 LRU 算法,由于 A 具有最低的时间记录 A(0),E 将取代 A。

算法需要跟踪何时最近一次使用了各个块,这个代价是昂贵的。实际上只要维持被访问的顺序就可以了,而不必知道确切的时间记录。我们可以用 STL 中的双向链表来实现这样一个功能。

题目参考 Leetcode 146

实现 get 和 put 两种方法,其中:

  • get(key) 返回 key 对应的 value(保证大于 0),如果没有则返回 -1。
  • put(key, value) 无返回值,若命中,将其 value 置为新值;若未命中,但 capacity 大于当前已存块数,直接插入;若未命中且数量已达 capacity,执行 LRU 算法进行替换。

借助 list 来记录块的使用情况,每一个新加入或命中的块都被放到头部,未命中时被替换掉的一定是位于尾部的节点。用一个 unordered_map 来存储 key 对应的 value 和在 list 中的位置(因为对 list 迭代器的删除操作不会导致其他迭代器失效),每次操作后都需要更新存储的 list 迭代器。

代码如下,具体细节参照注释:

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int value_1 = obj.get(key);
 * obj.put(key,value);
 */

class LRUCache {
public:
    LRUCache(int capacity) {
        cap = capacity;
        cache.reserve(cap);    // 预留 capacity 大小的空间
    }
    
    int get(int key) {
        auto iter = cache.find(key);
        if(iter != cache.end()) {    // 若命中
            touch(iter);    // 更新节点记录
            return iter->second.first;    // 返回 value 值
        }
        return -1;    // 未获取到返回 -1
    }
    
    void put(int key, int value) {
        auto iter = cache.find(key);
        if(iter == cache.end()) {    // 未命中
            if(cache.size() == cap) {    // cache 已满
                cache.erase(data.back());    // 从 cache 中删除位于 list 末尾的 key
                data.pop_back();    // 从 data 中弹出位于 list 末尾的 key
            }
            
            data.push_front(key);    // cache 无论满不满都需要,从头部加入新的 key,更新时间顺序记录
        }
        else {    // 命中
            touch(iter);    // 更新节点记录
        }
        cache[key] = {value, data.begin()};    // 未命中时置新块,命中时置新值
    }
    
private:
    list<int> data;
    unordered_map<int, pair<int, list<int>::iterator>> cache;
    int cap;
    /*
    命中时,将块在 list 中原来的节点删除,重新插入到头部。
    unordered_map 中存储的迭代器也需要修改。
    */
    void touch(unordered_map<int, pair<int, list<int>::iterator>>::iterator it) {
        int k = it->first;
        data.erase(it->second.second);
        data.push_front(k);
        it->second.second = data.begin();
    }
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 227,533评论 6 531
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,055评论 3 414
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 175,365评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,561评论 1 307
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,346评论 6 404
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,889评论 1 321
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,978评论 3 439
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,118评论 0 286
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,637评论 1 333
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,558评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,739评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,246评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,980评论 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,362评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,619评论 1 280
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,347评论 3 390
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,702评论 2 370

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,776评论 18 139
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,278评论 0 16
  • 生活本是美好的,阳光多么灿烂,天空多么明朗,活在世界上是多么的幸福。然而,人总是不能控制自己的忧愁,不能左右自己的...
    袁谦阅读 240评论 0 0
  • 今天一大早起来最重要的事就是上微博!找密码!然后关注傅园慧! 登录了好久都不上的新浪微博,搜索傅园慧,我硬是荣幸的...
    肥鱼胡说阅读 299评论 0 0
  • 今天是2018年4月18日,对我来说,是一个值得纪念的日子。我们小课题组的绘本阅读教学开始试讲了。这是我们...
    澜书童阅读 3,327评论 1 2