在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. contains 之 点击元素外位置隐藏元素

    contains 之 点击元素外位置隐藏元素 api: contains   检测一个元素包含在另一个元素之内  详解:http://www.runoob.com/jquery/misc-contai ...

  2. java.lang.IllegalMonitorStateException异常

    转自:https://blog.csdn.net/qianshangding0708/article/details/48290937

  3. MiniDao支持ID自增主键策略,使用讲解

    用法示例: /** * 插入数据(ID采用自增策略,并返回自增ID) * @param employee */ @IdAutoGenerator(generator="native" ...

  4. xlwt模块

    python使用xlwt模块操作Excel 该模块安装很简单 $ pip install xlwt 语法 import xlwt # 创建一个workbook 设置编码 workbook = xlwt ...

  5. React Native多语言

    https://medium.com/@danielsternlicht/adding-localization-i18n-g11n-to-a-react-native-project-with-rt ...

  6. 【转】 DOTA2中的伪随机及其lua实现

    因为单纯的随机确实会影响到竞技性,所以dota2引入的是伪随机机制,在大量的技能中,比如说混沌的混乱之箭.剑圣的剑舞.冰女的冰霜领域之类的技能,都利用了伪随机机制. 而纯随机,或者标准正态分布并不会因 ...

  7. hadoop搭建部署

    HDFS(Hadoop Distributed File System)和Mapreduce是hadoop的两大核心: HDFS(文件系统)实现分布式存储的底层支持 Mapreduce(编程模型)实现 ...

  8. angularjs 做不到实时脏值查询

    angularjs 做不到脏值查询 ,数据请求过来,不操作其他按钮,请求的值就是展示不出来:(相当于,只有手动触发,angularjs内部才会把脏值查询出来): 解决办法:在请求过来的值旁边加上$sc ...

  9. [原创]delphi一次性批量在TScrollBox中显示N个复选框TCheckBox的源码

    unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System ...

  10. C# Excel导数据

    遇到的几个坑,记录一下. 隐藏表,隐藏行,隐藏列, 单元格合并 => 拆分并填充内容, 显示隐藏列,结果那个列 还是不显示出来,拖动旁边的列也不显示. 结果在旁边的列上,按左右箭头键就出来了,也 ...