当前位置: 首页 > news >正文

算法中Hash备胎——LRU的设计与实现

核心内容1.理解LRU的原理
2.理解LRU是如何实现的
3.能够通过代码实现LRU

缓存是应用软件的必备功能之一,在操作系统、Java里的Spring、mybatis、redis、mysql等软件中都有自己的内部缓存模块,而缓存是如何实现的?

在操作系统教科书里我们知道常用的有FIFO、LRU和LFU三种基本的方式。FIFO也就是队列方式不能很好利用程序局部性特征,缓存效果比较差,一般使用LRU(最近最少使用)和LFU(最不经常食用讨要算法)比较多一些,LRU是淘汰最长时间没有被使用的页面,而LRU是淘汰一段时间内,使用次数最少的页面。

从实现上LRU是相对容易的,而LFU比较复杂,这里重点说明一下LRU的问题,也就是一道高频题目。

LeetCode146:链接

 1.LRU的含义

 关于什么是LRU,简单来说就是当内存空间满了,不得不淘汰某些数据时(通常是容量已满),选择最久未被使用的数据进行淘汰。

这里做了简化,题目让我们实现了一个容量固定的LRUCache。如果插入数据时,发现容器已经满了,则先按照LRU的规则淘汰一个数据,再将新数据插入,其中[插入]和[查询]都算作一次“使用”。

最近最少使用算法LRU是大部分操作系统为最大化页面命中率而广泛采用的一种页面置换算法。

该算法的思路是:发生缺页中断时,选择未使用时间最长的页面置换出去。假设内存只能容纳3个页面大小,按照7 0 1 2 0 3 0 4的次序访问页。假设内存按照栈的方式来描述访问时间,在上面的,是最近访问的,在下的是,最远时间访问的,LRU就是这样工作的。

2.Hash+双向链表实现LRU 

目前公认最合理的方式就是使用Hash+双链表。

  • Hash的作用是,用来做到O(1)访问元素,哈希表就是普通的哈希映射(HashMap),通过缓存数据的键映射到其在双链表中的位置。
  • Hash里的数据是key-value结构。value就是我们自己封装的node。key则是键值,也就是在Hash的地址。
  • 双向链表用来实现根据访问情况对元素进行排序。双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值是最近使用的,而靠近尾部的键值对就是最久未使用的。

这样一来,我们就要确认元素的位置直接访问哈希表就行了,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在O(1)的时间内完成get或者put操作。具体方法如

对于get操作,首先判断key是否存在:

  1. 如果key不存在,则返回-1
  2. 如果key存在,则key对应的结点是最近被使用的结点,通过哈希表定位到该结点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该结点的值

对于put操作,首先判断key是否存在:

  1. 如果key不存在,使用key和value创建一个新的结点。在双向链表的头部添加该结点,并将key和该结点添加进哈希表中。然后判断双向链表的结点数是否超出容量,如果超出容量,则删除双向链表的尾部结点,并删除哈希表中对应的项。
  2. 如果key存在,则跟get操作类似,先通过哈希表定位,再将对应的结点的值更新为value,并将该结点移动到双向链表的头部。

上述各项操作中,访问哈希表的时间复杂度为O(1),在双向链表的头部添加节点、在双链表的尾部删除节点的复杂度也为O(1)。

而将一个结点移动到双向链表的头部,可以分成[删除节点]和[在双向链表的头部添加节点]两部操作,都可以在O(1)时间内完成。

同时为了方便操作,在双向链表的实现中,使用一个伪头部(dummyhead)和伪尾部(dummytail)标记界限,这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在。

        

相关文章:

  • Spring Boot 配置文件加载优先级全解析
  • java方法04:命令行传递参数
  • Linux 内存映射机制:正向映射与反向映射深度解析
  • LeetCode零钱兑换(动态规划)
  • MYSQL数据库语法补充2
  • Rancher 全面介绍
  • 《P2660 zzc 种田》
  • 创建一个简单的HTML游戏站
  • JS 数组相同的key 进行合并
  • 【强化学习】时间差分(Temporal Difference, TD)
  • OpenCv高阶(一)——图像金字塔(上采样、下采样)
  • 探秘AI(002)之“文心一言(文小言)”
  • Linux普通用户怎么切换为root用户
  • 如何避免论文内容被误认为是 AI 生成的?
  • 【第一天】 OSG初探——环境搭建与第一个3D窗口
  • 大模型的输出:温度对输出的影响
  • 开发效率提升200%——cursor
  • Windows Anaconda使用Sentence-BERT获取句子向量
  • 驱动-创建设备节点
  • Spring MVC与Spring Boot文件上传配置项对比