在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. C#USB设备枚举Kernel32的PInvoke

    using System; using System.Runtime.InteropServices; using System.Security; namespace Splash { #regio ...

  2. 【转】Jenkins 二次开发 - Python

    马克,备用: Jenkins 二次开发 https://testerhome.com/topics/14988?locale=zh-TW python-jenkins api 文档:https://p ...

  3. TFrame bug

    delphi 10.1.2 工程里有很多fram 正确的工程文件dproj中fram的定义是 <DCCReference Include="Unit15frame.pas"& ...

  4. synchronized 和 lock 的区别

    1.Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性.Lock是一个类,通过这个类可以实现同步访问: 2.Lock和synchronized有一点非常大的 ...

  5. 机器学习进阶-案例实战-停车场车位识别-keras预测是否停车站有车

    import numpy import os from keras import applications from keras.preprocessing.image import ImageDat ...

  6. day43-socketserver

    基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环 socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题) server类: reque ...

  7. java 浅克隆(浅复制)和深克隆(深复制)

    http://www.voidcn.com/blog/u011380813/article/p-6161450.html https://gold.xitu.io/entry/570d89651ea4 ...

  8. pod引用第三方库的几种方式

    pod引用库的原理,本质上是去找.podspec文件,podspec中包含库的地址及最新的版本号(tag标签),如果pod时没有指定版本,则pod install时会去下载podspec文件中指定的最 ...

  9. swarm on ubuntu

    转自:https://www.cnblogs.com/jsonhc/p/7862518.html 由于在centos7系统上使用docker-machine安装的swarm实现服务的负载均衡一直失败, ...

  10. C语言基础入门

    搭建Windows平台C/C++开发环境 第1步: 第2步: 第3步: 第4步: 第5步: 第6步: 第7步: 第8步: 第9步: 第10步: 第11步: 第12步: 第13步: 第14步: 第15步 ...