LRU原理

在一般标准的操作系统教材里,会用下面的方式来演示 LRU 原理,假设内存只能容纳3个页大小,按照 7 0 1 2 0 3 0 4 的次序访问页。假设内存按照栈的方式来描述访问时间,在上面的,是最近访问的,在下面的是,最远时间访问的,LRU就是这样工作的。

但是如果让我们自己设计一个基于 LRU 的缓存,这样设计可能问题很多,这段内存按照访问时间进行了排序,会有大量的内存拷贝操作,所以性能肯定是不能接受的。

那么如何设计一个LRU缓存,使得放入和移除都是 O(1) 的,我们需要把访问次序维护起来,但是不能通过内存中的真实排序来反应,有一种方案就是使用双向链表。

基于 HashMap 和 双向链表实现 LRU

整体的设计思路是,可以使用 HashMap 存储 key,这样可以做到 save 和 get key的时间都是 O(1),而 HashMap 的 Value 指向双向链表实现的 LRU 的 Node 节点。

LRU 存储是基于双向链表实现的,下面的图演示了它的原理。其中 head 代表双向链表的表头,tail 代表尾部。首先预先设置 LRU 的容量,如果存储满了,可以通过 O(1) 的时间淘汰掉双向链表的尾部,每次新增和访问数据,都可以通过 O(1)的效率把新的节点增加到对头,或者把已经存在的节点移动到队头。

下面展示了,预设大小是 3 的,LRU存储的在存储和访问过程中的变化。为了简化图复杂度,图中没有展示 HashMap部分的变化,仅仅演示了上图 LRU 双向链表的变化。我们对这个LRU缓存的操作序列如下:

save("key1", 7)
save("key2", 0)
save("key3", 1)
save("key4", 2)
get("key2")
save("key5", 3)
get("key2")
save("key6", 4)

相应的 LRU 双向链表部分变化如下:

总结一下核心操作的步骤:

save(key, value),首先在 HashMap 找到 Key 对应的节点,如果节点存在,更新节点的值,并把这个节点移动队头。如果不存在,需要构造新的节点,并且尝试把节点塞到队头,如果LRU空间不足,则通过 tail 淘汰掉队尾的节点,同时在 HashMap 中移除 Key。
get(key),通过 HashMap 找到 LRU 链表节点,因为根据LRU 原理,这个节点是最新访问的,所以要把节点插入到队头,然后返回缓存的值。

完整基于 C++的代码参考如下

#include <iostream>
#include <map>
using namespace std;struct DLinkList
{int key, val;DLinkList *pre;DLinkList *next;DLinkList(): key(0), val(0), pre(NULL), next(NULL){}DLinkList(int _key, int _val): key(_key), val(_val), pre(NULL), next(NULL){}
};class LRUCache
{
private:map<int, DLinkList *> maap;DLinkList *head;DLinkList *tail;int size;int cap;public:LRUCache(int capicity){head = new DLinkList();tail = new DLinkList();head->next = tail;tail->pre = head;size = 0;cap = capicity;}int Get(int key){if (!maap.count(key)){return -1;}DLinkList *node = maap[key];MoveToHead(node);return node->val;}void MoveToHead(DLinkList *node){ReMoveNode(node);AddToHead(node);}//从双链表中删除void ReMoveNode(DLinkList* node){node->pre->next=node->next;node->next->pre=node->pre;}void AddToHead(DLinkList* node){node->next=head->next;node->pre=head;head->next->pre=node;head->next=node;}void  Save(int key,int value){if(maap.count(key)){DLinkList* node=maap[key];MoveToHead(node);node->val=value;}else{DLinkList* node=new DLinkList(key,value);AddToHead(node);maap[key]=node;size++;if(size>cap){DLinkList* delNode=ReMoveTail();maap.erase(delNode->key);delete delNode;size--;}}}DLinkList* ReMoveTail(){DLinkList* node=tail->pre;ReMoveNode(node);return node;}
};

LRU原理及其实现(C++)相关推荐

  1. redis的lru原理_Redis的LRU机制介绍

    高并发架构系列:Redis的内存回收原理,及内存过期淘汰策略详解 Redis内存回收机制 Redis的内存回收主要围绕以下两个方面: 1.Redis过期策略删除过期时间的key值 **2.Redis淘 ...

  2. redis的lru原理_Redis的LRU算法

    Redis的LRU算法 LRU算法背后的的思想在计算机科学中无处不在,它与程序的"局部性原理"很相似.在生产环境中,虽然有Redis内存使用告警,但是了解一下Redis的缓存使用策 ...

  3. linux页面算法源码,LRU算法原理解析

    LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的. 现代操作系统提供了一种对主存的抽象概念虚拟内存,来对主存进行更好地管理.他将主存 ...

  4. Python实现:详解LRU缓存淘汰算法

    大家好,今天我们和大家聊一个非常常用的算法,叫做LRU. LRU的英文全称是Least Recently Used,也即最不经常使用.我们看着好像挺迷糊的,其实这个含义要结合缓存一起使用.对于工程而言 ...

  5. 算法之最近最少使用LRU

    layout: post title: "算法之最近最少使用LRU" subtitle: " "1.HashMap,存放数据 2.双向链表节点,确保最新被使用的 ...

  6. 缓存淘汰策略—LRU算法(java代码实现)

    LRU 原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也更高" ...

  7. 缓存淘汰算法--LRU算法

    缓存淘汰算法--LRU算法 参考: https://www.cnblogs.com/dailidong/p/7571178.html https://blog.csdn.net/wangxilong1 ...

  8. LRU(Least Recent Used) java实现——为什么采用HashMap+双向链表

    在知乎上看到一篇文章 : LRU原理和Redis实现--一个今日头条的面试题 他采用HashMap+双向链表实现LRU(淘汰掉最不经常使用的).先来将原文简单引用介绍下,以免原作者删除. 很久前参加过 ...

  9. 【039期】头条面试:说一说 LRU 原理和 Redis 如何实现?

    >>号外:关注"Java精选"公众号,回复"面试资料",免费领取资料!"Java精选面试题"小程序,3000+ 道面试题在线刷, ...

最新文章

  1. 【iOS】iOS 调试快速定位程序在哪崩溃
  2. 1-3.监督学习(supervised learning)
  3. (第九周)团队项目14
  4. Python 文件读写
  5. 对齐输出(信息学奥赛一本通-T1003)
  6. Java—接口(工厂模式代理模式)
  7. Python学习-储存器
  8. APMCon2017 | 一大波技术大神来袭,你要的性能优化干货这里全都有
  9. 网络服务器ssh、ftp、telnet、samba配置总结
  10. 对JS中this的理解
  11. java mov 转 mp4 视频格式
  12. P1894 [USACO4.2]完美的牛栏The Perfect Stall
  13. React - Router的基本使用介绍
  14. curl php 用途,PHP使用CURL详解
  15. 增量型编码器与绝对值编码器
  16. 计算机连接打印机用户数量修改,win7系统下局域网如何限制每台打印机的使用成员数量...
  17. vue 百度地图获取经纬度地址
  18. stm32出现ram、rom不够用,调试方法
  19. 【转】Windows Linux MacOS操作系统的区别
  20. dpi和ppi换算_八一八那些px、pt、ppi、dpi、dp、sp之间的关系

热门文章

  1. 陶陶的兔二,建好啦!
  2. 使用Custom.pll修改标准Form的LOV
  3. 关于unix下使用tar的一些常用技巧
  4. 详解JRE和JDK的区别
  5. Javascript网站繁简转换解决方案
  6. CodeForces - 1359D Yet Another Yet Another Task(最大连续子段和)
  7. MMDetection-数据准备
  8. Lua 调试(Debug)
  9. 7.IDA-创建结构体
  10. 对现有的所能找到的DDOS代码(攻击模块)做出一次分析----自定义攻击篇