LRU

Redis的内存淘汰机制好几种,如ttl、random、lru。

lru(less recently used)即最近最少使用策略,表示在最近一段时间内最少被使用到的Redis键,如果遇到内存不足,会有限淘汰这部分键来腾出更多空间。

今天就来说说lru这种淘汰策略是如何通过链表这种结构实现的。

难点

在链表结构中,如何表示最近访问的节点和如何表示最久没有访问的节点?

如何判定一个链表中是否存在要查找的节点?

如何向链表结构插入节点,并放在最近最新的节点位置?

如果在链表中删除一个节点?

思路

结合上面的难点,我们可以构建一个可以解决问题的链表模型。

如何表示最近访问的节点和如何表示最久没有访问的节点

可以设计一个双向链表,头结点表示最近访问的节点,尾结点表示最久没有访问的节点。使用双向链表是为了查找和定位更加方便。

如何判定一个链表中是否存在要查找的节点

解决这个问题,最直接的思路就是遍历整个链表,依次匹配如果找到相同的值,对应的节点就是待查找的节点,如果遍历完整个链表,还是没有找到,表示该链表不存在该节点。

还有一种思路是将链表的所有节点存放到一个map结合中,查找的时候直接通过map的key进行查找即可。

如何向链表结构插入节点,并放在最近最新的节点位置

结合前面几篇,我们知道,链表的插入和删除是非常方便的,但是在lru问题背景下,如果插入节点并保证是最新的位置呢?显然最新的节点是要放到头结点的。

另外需要注意的点是,插入之前需要先查找这个节点是否存在链表中,如果存在需要先删除。

如果在链表中删除一个节点

删除一个节点的前置步骤应该是先判定一个节点是否存在链表中,如果存在删除即可,如果不存在则无需删除。

通过以上几个问题,我们大概可以构想出几个原子函数

  • 初始化双向链表结构
  • 查找指定节点
  • 插入指定节点
  • 删除指定节点

下面我们主要看如何实现这几个函数就可以了,主要代码如下

type LRUCache struct {
Cap int
Map map[int]*Node
Head *Node
Last *Node
} type Node struct {
Val int
Key int
Pre *Node
Next *Node
} func Constructor(capacity int) LRUCache {
cache := LRUCache{
Cap: capacity,
Map: make(map[int]*Node, capacity),
Head: &Node{},
Last: &Node{},
}
cache.Head.Next = cache.Last
cache.Last.Pre = cache.Head
return cache
} func (this *LRUCache) Get(key int) int {
node, ok := this.Map[key]
if !ok {
return -1
}
this.remove(node)
this.setHeader(node)
return node.Val
} func (this *LRUCache) Put(key int, value int) {
node, ok := this.Map[key]
if ok {
this.remove(node)
} else {
if len(this.Map) == this.Cap {
delete(this.Map, this.Last.Pre.Key)
this.remove(this.Last.Pre)
}
node = &Node{Val: value, Key: key}
this.Map[node.Key] = node
}
node.Val = value
this.setHeader(node)
} func (this *LRUCache) setHeader(node *Node) {
this.Head.Next.Pre = node
node.Next = this.Head.Next
this.Head.Next = node
node.Pre = this.Head
} func (this *LRUCache) remove(node *Node) {
node.Pre.Next = node.Next
node.Next.Pre = node.Pre
}

  

初始化的双向链表如上图所示,一个节点包括数据部分data,前继节点pre和后继节点next。

所有节点数据放入map集合中。

Get()方法会在map中查找,如果不存在,则直接返回。如果存在,则调用remove先删除该节点,再调用setHeader将节点放入头结点。

Put()方法会首先在map中查找对应节点,如果找到,则先调用remove删除方法删除改节点,并调用setHeader方法将节点放入头结点。

至此一个lru的淘汰策略使用一个双向链表就实现了。

链表总结

前面几篇,分别介绍了通过链表结构如何实现链表反转、判断链表是否有环、链表结构的回文判断、有序链表的合并以及本篇的lru实现。

链表具备插入删除方便,但是查找效率较低的特性。

以下是对于链表结构的梳理

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

【日拱一卒】链表——如何实现lru的更多相关文章

  1. 别做操之过急的”无效将军”,做实实在在的”日拱一卒” zz

    别做操之过急的”无效将军”,做实实在在的”日拱一卒” 前天在网上看到一句话很不错,拿来和大家分享,同时用我的“大叔”三观来解读这句话. 这句话是:“我们不需要操之过急的”无效将军”,我们需要实实在在的 ...

  2. Windows Server 2008 网管数据采集 努力做“日拱一卒“

    Windows Server 2008R2系统管理[刘道军主讲MCITP课程] http://edu.51cto.com/course/course_id-510.html Windows Serve ...

  3. lintcode刷题笔记(一)

    最近开始刷lintcode,记录下自己的答案,数字即为lintcode题目号,语言为python3,坚持日拱一卒吧... (一). 回文字符窜问题(Palindrome problem) 627. L ...

  4. 比较两个文件不同以及生成SQL插入语句

    Tips 做一个终身学习的人! 日拱一卒,功不唐捐. 今天有个小小的需求,具体需求是这样的: 有两个文本文件,每个文件都有一些字符串文本: 比较第一个文件中,在第二个文件中,不存在的字符串文本: 把这 ...

  5. 1. Apache ZooKeeper快速课程入门

    Tips Tips做一个终身学习的人! 日拱一卒,功不唐捐. 在过去的几十年里,互联网改变了我们生活的方式.Internet上提供的服务通常由复杂的软件系统支持,这些系统跨越了大量的服务器,而且常常位 ...

  6. 2.动手实操Apache ZooKeeper

    Tips 做一个终身学习的人! 日拱一卒,功不唐捐. 在本节中,我们将讲解如何下载并安装Apache ZooKeeper,以便我们可以直接开始使用ZooKeeper. 本部分旨在通过提供详细的安装和使 ...

  7. 我的$OI$

    我只是懒得写日记啦\(\color{pink}{qwq}\) //11月8日 啊--终于要\(NOIp\)了,为此期盼了好久.紧张了好久的我,不知道会迎来怎样的结果. 我只知道这段回忆是值得保留封存的 ...

  8. NSDate 时间加减

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/pearlhuzhu/article/details/26227393 NSDate有个类别,例如以下 ...

  9. Atitit.js this错误指向window的解决方案

    Atitit.js this错误指向window的解决方案 1.1. 出现地点and解决之道1 1.2. call,apply和bind这三个方法2 1.2.1. Function.prototype ...

随机推荐

  1. 用 shell 脚本做 restful api 接口监控

    问题的提出 基于历史原因,公司有一个"三无"采集服务--无人员.无运维.无监控--有能力做的部门不想接.接了的部门没能力.于是就一直这样裸奔,直到前几天一个依赖于这个采集服务的大数 ...

  2. centos7 安装k8s kubectl 客户端

    1. 配置k8s的kubelet 管理客户端 1 cat <<EOF > /etc/yum.repos.d/kubernetes.repo 2 [kubernetes] 3 name ...

  3. C#数据结构-静态链表

    对于双向链表中的节点,都包括一个向前.向后的属性器用于指向前后两个节点,对于引用类型,对象存储的是指向内存片段的内存指针,那么我们可以将其简化看作向前向后的两个指针. 现在我们将引用类型替换为值类型i ...

  4. CSS常见反爬技术

    目录 利用字体 反爬原理 应对措施 难点: 利用背景 反爬原理 应对措施 利用伪类 反爬原理 应对措施 利用元素定位 反爬原理 应对措施 利用字符切割 反爬原理 应对措施 利用字体 反爬原理 反爬原理 ...

  5. 笔趣阁小说 selenium爬取

    import re from time import sleep from lxml import etree from selenium import webdriver options = web ...

  6. 一文读懂Redis常见对象类型的底层数据结构

    Redis是一个基于内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.Redis支持五种常见对象类型:字符串(String).哈希(Hash).列表(List).集合(Set)以及有序集合( ...

  7. UI-个人作品集

    前言 现在需要将之前做过的UI设计集起来,并做些好看的设计 设计思路 开头>技能>作品>结束 开头 我使用线条来构图 以及比较融合的背景进行衬托主题 技能 通过文字与图形搭配展示出我 ...

  8. eclipse 包与子包的视图显示方式切换

    上图Package Presentation ---> Hierarchical(如下图显示父包与子包) 参考:https://zhidao.baidu.com/question/2205086 ...

  9. java 第五课 异常

    1.为什么使用异常? 若没有异常处理机制,会使用流程控制语句if  switch等来处理异常情况,程序复杂 2.捕捉异常try catch finally 3.方法中抛出异常throw(throw 可 ...

  10. 020_Typora使用

    目录 Typora使用 下载 安装 设置 使用 Typora使用 Typora插入本地图片为本地路径,网络上无法查看,所以建议只插入网络图片. 下载 百度搜索官网 下载 安装 设置 视图->显示 ...