在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. 坑爹的myeclipse 的tomcat 重部署 redeploy !

    启动 tomcat 出现: Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bea ...

  2. mybatis三(关联查询)

    一.类属性 @Alias("depart")public class Department { private Integer id; private String departN ...

  3. spark 练习

    scala> import org.apache.spark.SparkContext import org.apache.spark.SparkContext scala> import ...

  4. leetcode1003

    class Solution: def isValid(self, S: str) -> bool: n = len(S) if n % 3 != 0: return False while n ...

  5. VC开发中一些问题的解决

    (1) MFC:在视频窗口上显示一个图片: 使用button显示图片的话,鼠标点击好图片(按钮)上,窗口无法响应. 应该使用一个static静态控件来显示,这样不会妨碍视频窗口对鼠标消息的响应. (2 ...

  6. 缓存框架有使用过哪些?memcache和redis有什么区别?项目中,怎么去选择?

    缓存有:ehcache,memcache和redis等 区别: 1. Redis和Memcache都是将数据存放在内存中,都是内存数据库.不过memcache还可用于缓存其他东西,例如图片.视频等等. ...

  7. wget 递归下载整个网站(网站扒皮必备)

    有时间看到别人网站的页面比较漂亮,就想给扒皮下来,学习学习.分享一个我常用网站扒皮命令wget 这个命令可以以递归的方式下载整站,并可以将下载的页面中的链接转换为本地链接. wget加上参数之后,即可 ...

  8. 深度学习原理与框架-卷积网络细节-经典网络架构 1.AlexNet 2.VGG

    1.AlexNet是2012年最早的第一代神经网络,整个神经网络的构架是8层的网络结构.网络刚开始使用11*11获得较大的感受野,随后使用5*5和3*3做特征的提取,最后使用3个全连接层做得分值得运算 ...

  9. JEECG 3.7.3 新春版本发布,企业级JAVA快速开发平台

    JEECG 3.7.3新春版本发布 -  微云快速开发平台 导读           ⊙精美Echart报表 ⊙二维码生成功能 ⊙Online接口改造采用JWT机制 ⊙智能菜单搜索 ⊙代码生成器模板优 ...

  10. day3 python 阿狸的进阶之路

    函数概念: 1.为什要有函数 组织结构不清晰,可读性差,代码冗余,可扩展性差. 2.什么是函数   具备某一个功能的工具--->函数     事先准备工具->函数的定义     拿来就用. ...