在Leetcode上遇到了两个有趣的题目,分别是利用LRU和LFU算法实现两个缓存。缓存支持和字典一样的get和put操作,且要求两个操作的时间复杂度均为O(1)。

  首先说一下如何在O(1)时间复杂度内实现get方法。据鄙人所知,对于没有限定数据范围的数据,唯一拥有O(1)时间复杂度的get的数据结构就是哈希表,尽管其时间复杂度是通过概率来推算出来的。因此毋庸质疑,LRU和LFU的get方法中必定使用了哈希表的取值,相应的put方法中也必定调用了哈希表的赋值操作。

  由于LRU和LFU都涉及到了选择被淘汰记录和淘汰操作。能在O(1)时间内选择被淘汰记录的数据结构非常类似于队列,队列中的元素按插入时间的先后顺序进行排序,这样在插入最新记录和删除最老记录上都能获得O(1)的时间复杂度。但是这还远远不够,因为存在更新操作,即当使用get方法访问一条记录,其最后访问时间将被更新到现在。这也意味着必须从队列中删除某个元素并将其加入到队列尾部这种操作的存在,使用一般的数组队列是无法在O(1)时间复杂度内完成这个操作的。这样显然必须考虑另外一个数据结构,链表,链表能在O(1)时间复杂度内删除和加入任意记录。

   LRU是least recently used的缩写,即最近最久未使用。在缓存数据量溢出时,必须淘汰某一项记录才能插入新的记录,而LRU算法则选择淘汰上一次使用时间距离现在最久的那条记录。只需要同时维护一个链表和一个哈希表,下面说明各个操作的具体流程:

  get:

    如果哈希表中存在对应记录,则将其从链表中移除并加入到链表尾部。

  put:

    调用get方法获取对应的记录,如果记录非空,则直接修改值。否则执行下一步。

    同时从链表和哈希表中移除链表头部记录(淘汰操作),并向链表尾部和哈希表中加入新的记录。

  LFU是least frequently used的缩写,即最少被使用。在缓存已满且需要加入新的记录时,选择缓存中被使用次数最少的记录(如果有多条,则选择其中使用时间距离现在最久的记录)。

  如果LRU是简洁美好的,那么LFU就要费力的多了。LFU的实现的难点在于,要保证记录按照使用次数,和最后访问时间双项指标进行排序。由于记录被访问而修正记录的使用次数和最后访问时间,需要找到合适的插入位置,这无法通过一个链表实现。我的做法是使用一个保存链表的链表(二维链表)以及一个哈希表,二维链表中每一个保存的链表中所有记录都有相同的访问次数,且按照最后访问时间先后排序。下面说明各个操作的具体流程:

  move(N, L):

    如果链表L为null,则新建一个链表加入到二维链表尾部。之后进行下一步。

    如果L为空或者L所有元素的访问次数与N的访问次数一致,则将N加入到L的尾部并结束流程。

    否则创建一个新的链表newL,并将newL插入到二维链表中,插入位置是L的前面。将N加入到newL中并结束流程。

  get:

    如果哈希表中有对应记录R,则将R先从自己所在的链表L中移除。并调用move方法移动R到L在二维链表中的后面一个兄弟中。

  put:

    调用get方法取关键字对应的记录,如果存在则更新值并返回。否则进行下一步。

    先选择二维链表中为首的链表中的第一条记录,进行淘汰(分别从链表和哈希表中移除)。

    之后调用move方法将新的记录R移动到二维链表为首的链表中。并将R加入到哈希表中。

  注意从一个链表中移除一条记录可能会导致链表为空(即链表原先只有一个记录),则链表本身的存在就失去了意义(链表存在的意义是组织拥有相同访问次数的记录),因此这时候可以回收空链表,这样同时可以将LFU的空间复杂度限制到O(capacity)内,capacity是缓存的容量。

  下面说明LRU和LFU的时空复杂度。LRU和LFU的所有操作均只涉及常数次链表操作和常数次哈希表操作,因此时间复杂度均为O(1)。而空间复杂度,LRU使用了链表和哈希表,二者的空间复杂度均为O(capacity),因此总的空间复杂度为O(capacity)。而LFU算法的空间复杂度则取决于二维链表的长度,由于前面保证了每个二维链表中的链表都非空,也就是说每个链表都至少对应一条记录,而总记录数不超过capacity,故链表数也被限制到了O(capacity)内,因此LFU算法的空间复杂度为O(capacity)。

Leetcode:LRU Cache,LFU Cache的更多相关文章

  1. leetcode 146. LRU Cache 、460. LFU Cache

    LRU算法是首先淘汰最长时间未被使用的页面,而LFU是先淘汰一定时间内被访问次数最少的页面,如果存在使用频度相同的多个项目,则移除最近最少使用(Least Recently Used)的项目. LFU ...

  2. [LeetCode] LFU Cache 最近最不常用页面置换缓存器

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  3. [LeetCode] LRU Cache 最近最少使用页面置换缓存器

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  4. LeetCode LFU Cache

    原题链接在这里:https://leetcode.com/problems/lfu-cache/?tab=Description 题目: Design and implement a data str ...

  5. [LeetCode] 460. LFU Cache 最近最不常用页面置换缓存器

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  6. Leetcode: LFU Cache && Summary of various Sets: HashSet, TreeSet, LinkedHashSet

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  7. LFU Cache

    2018-11-06 20:06:04 LFU(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”. ...

  8. 内存淘汰机制——LRU与LFU

    内存淘汰机制之LRU与LFU LRU(Least Recently Used):淘汰 近期最不会访问的数据 LFU(Least Frequently Used):淘汰 最不经常使用(访问次数少) 所谓 ...

  9. 缓存算法(FIFO 、LRU、LFU三种算法的区别)

    FIFO算法 FIFO 算法是一种比较容易实现的算法.它的思想是先进先出(FIFO,队列),这是最简单.最公平的一种思想,即如果一个数据是最先进入的,那么可以认为在将来它被访问的可能性很小.空间满的时 ...

随机推荐

  1. Python基础4 迭代器、装饰器、软件开发规范

    本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 1.列表生成式,迭代器&生成器 列表生成式 孩子,我现在有个需 ...

  2. maven项目里的mapper不被加载,解析

    出现这个错误是因为maven加载配置文件是从resource里加载的,所以要配置一下

  3. JS 事件 Event

    注册事件 target.addEventListener(type, listener, options); 或者 target.addEventListener(type, listener, us ...

  4. 获取数据库表中自增长最新的id

    mybatis <insert id="InsertCourse"> insert into training_course(type_id,course_title, ...

  5. Delphi 泛型详解

    http://www.cnblogs.com/jxgxy/category/216671.html

  6. Zookeeper原理架构

    Zookeeper到底是什么!? 学一个东西,不搞明白他是什么东西,哪还有心情学啊!! 首先,Zookeeper是Apache的一个java项目,属于Hadoop系统,扮演管理员的角色. 然后看到官网 ...

  7. etcd集群故障处理(转)

    1. etcd安装 rpm -ivh etcd-3.2.15-1.el7.x86_64.rpm systemctl daemon-reload systemctl enable etcd system ...

  8. RESET MASTER和RESET SLAVE使用场景和说明,以及清除主从同步关系

    mysql主从复制中,需要将从库提升为主库,需要取消其从库角色,这可通过执行RESET SLAVE ALL清除从库的同步复制信息.包括连接信息和二进制文件名.位置.从库上执行这个命令后,使用show ...

  9. html页面调用servlet中文乱码问题

    1.需要在html中:<meta charset=utf-8" /> 2.在servlet的doPost方法中 首先:response.setContentType(" ...

  10. Linux创建SSH信任关系

    Linux服务器创建信任关系可以解决远程执行命令.远程传输文件多次手工输入的麻烦.可以实现环境一键打包备份. 测试环境 SuSE 手工创建 假设服务器A与B间要建立信任关系.用户想从服务器A免密码登录 ...