LRU,即最近最少使用,在 redis 里缓存的淘汰策略就有用到这一个算法,今天我们自己来实现一个 LRU,并且存取时间复杂度控制在 O(1)文字好像有点多,代码也会有

一、题意分析

通常我们会把频繁用到的数据放到缓存里,这样取数据比较快,但内存有限,所以经常会有一些淘汰策略,LRU就是其中一种,他的原理是:我们认为最近访问(包括 get 和 set)操作的数据,最有可能是接下来即将用到的数据,当达到一定数量时,我们淘汰掉最近都没有访问的数据

这里需要注意的是,get 操作也算是“访问”了一次数据,显然 put 也算,因为最近插入的数据,极大可能是我马上要用到的数据

其实想要单纯实现是比较简单的,题目难点在于存取时间复杂度的要求是 O(1)

二、实现原理

主要是数据结构的选取,我们可以简单来分析下:

首先存数据,时间复杂度为 O(1),如果是简单的追加数据,链表和数组都可以,但因为需要体现“最近访问”,所以很大可能需要移动数据,那这时候数组就不是很适合了,链接倒是一个不错的选择

其次取数据,数组按下标取出,时间复杂度确实是 O(1),但显然我们这里是根据 key 去取对应的 value,很容易想到 python 里的 dict 类型

综上,我们采用的是链表 + 字典的组合。

链表存数据,字典也存在数据,这样显然会有很多问题,比如怎么快速根据 key 找到对应的链表?因此我们换一种思路,链表存取数据,包括key 和 value,而字典格式为 {key: node},即 key 和 对应的链表结点,这样就符合题目要求了

三、呈上代码

下面的实现还是有点不科学,首结点和尾结点没有用到循环链表(因为一开始指针问题思考错误,所以没有科学用于循环链表),但还是实现了,勉强可看:

class CircleLinkNode:

"""双向链接,最先访问的放至尾部"""

def __init__(self, key, value):

self.value = value

self.key = key

self.previous = None

self.next = None

def insert(self, node):

"""尾部插入"""

self.next = node

node.previous = self

def remove_to_end(self, last_node, root_node):

"""将当前结点移至最后"""

# 如果当前结点是尾部结点,直接return

if self.next is None:

return

# 如果是头部结点

if self.previous is None:

root_node.next = self.next

self.next.previous = None

self.next = None

self.previous = last_node.previous

last_node.previous.next = self

last_node.previous = self

return

self.previous.next = self.next

self.next.previous = self.previous

self.previous = last_node.previous

last_node.previous.next = self

self.next = None

last_node.previous = self

class LRUCache:

def __init__(self, capacity: int):

self._root_node = CircleLinkNode(None, None) # 头结点

self._cnode = CircleLinkNode(None, None) # 当前结点

self._res_dict = {}

self._cur_len = 0

self._capacity = capacity

def get(self, key: int) -> int:

# 更新链表结构

cur_node = self._res_dict.get(key)

if cur_node:

cur_node.remove_to_end(self._cnode, self._root_node)

return cur_node.value

return self._res_dict.get(key, -1)

def put(self, key: int, value: int) -> None:

# 如果当前不存在值

if self._cur_len == 0:

new_node = CircleLinkNode(key, value)

self._cnode.previous = new_node

self._root_node.next = new_node

self._res_dict[key] = new_node

self._cur_len += 1

return

# 如果put的值在缓存中存在

cur_node = self._res_dict.get(key)

if cur_node:

# 将当前结点移至尾部结点

cur_node.value = value

cur_node.remove_to_end(self._cnode, self._root_node)

return

# 如果put的值不在缓存中不存在并且长度不饱和

if self._cur_len < self._capacity:

self._cur_len += 1

new_node = CircleLinkNode(key, value)

self._cnode.previous.insert(new_node)

self._cnode.previous = new_node

self._res_dict[key] = new_node

return

# 如果put的值不在缓存中,且长度饱和

# 先向后追加

new_node = CircleLinkNode(key, value)

self._cnode.previous.insert(new_node)

self._cnode.previous = new_node

temp_node = self._root_node.next

self._res_dict[key] = new_node

self._root_node.next = temp_node.next

temp_node.next.previous = None

temp_node.next = None

del self._res_dict[temp_node.key]

附上leetcode题目地址, 中英文均有:

微信搜索:Python猫聊,关注更多Python信息

java 带超时时间lru_如何在O(1)时间复杂度下实现LRU相关推荐

  1. Redisson(2-2)分布式锁实现对比 VS Java的ReentrantLock之带超时时间的tryLock

    Redisson实现了一整套JDK中ReentrantLock的功能,这里对比一下实现的差异和核心的思想. unfair模式的带超时时间的tryLock(超时时间) ReentrantLock 这里上 ...

  2. java事务超时时间,java – 如何在WebSphere Liberty Batch中配置事务超时?

    > javax.transaction.global.timeout的作用是什么? >我是否需要在CheckpointAlgorithm中实现checkpointTimeout()方法? ...

  3. java socket超时时间_java中socket 默认超时时间 | 学步园

    MS:永远吧 Java代码 connect publicvoidconnect(SocketAddress endpoint, inttimeout) throwsIOException Connec ...

  4. Java 如何设置时间_如何在Java中设置尊重用户操作系统设置的日期和时间格式

    我在Windows 7机器上运行Java应用程序,在该机器上,我的区域设置已设置为将日期格式设置为YYYY-mm- dd,将时间格式设置为HH:mm:ss(例如" 2011-06-20 07 ...

  5. java ssh 超时时间,Java Ganymed Trlead Orion ssh会话超时

    我正在使用SSH Ganymed库别名Trilead别名Orion. 我试图理解会话的确切行为,因为我需要保持ssh连接打开很长时间(也许永远),并在我的jvm关闭或类似的事情时关闭它. 所以,我的问 ...

  6. java web超时提醒_如何在Java Web应用程序中动态设置会话超时?

    我需要给我的用户一个Web界面来更改会话超时间隔. 因此,Web应用程序的不同安装对其会话可以具有不同的超时,但是它们的web.xml不能相同. 您的问题很简单,您需要在运行时配置会话超时间隔,并且应 ...

  7. Spring Boot内置Tomcat设置超时时间

    最近有个小工程扫描出一个安全漏洞, SlowHttp慢速攻击的,需要修改 Tomcat 的配置,也正好关于 Tomcat 的参数调优,正好记录一下. 漏洞信息 查了一下这个漏洞,漏洞有两个解决方法, ...

  8. Java 调用接口工具类并设置请求和传输超时时间

    废话不多说直接上代码 /*** 接口调用工具类* @Author: MonsterTiny* @Date: 2020-07-23 10:06*/ public class HttpClientUtil ...

  9. Java两则故障分析和常见连接超时时间

    郑昀 汇总 20130309 常见现象的故障分析: 现象倒推一:Java Web应用的连接数暴增 最大的可能是,Web应用的线程调用路径中阻塞在某个远端资源上. 线程向某个远端资源发起的请求被阻塞,可 ...

最新文章

  1. 也谈SAP业务顾问如何避免被ABAP开发顾问怒打
  2. boot数据加解密 spring_springboot项目使用druid对数据库密码的加解密
  3. css4个伪元素,CSS_CSS3中的content属性使用示例,CSS中主要的伪元素有四个:befo - phpStudy...
  4. 求两个字符串的最长公共字串(连续)
  5. mysql 挑战握手协议_什么是挑战握手认证协议协议,在现实中有哪些应用?
  6. 阿里云服务器ECS按ctrl+alt+delete无法登录
  7. Put-Me-Down项目Postmortem2
  8. 【youcans 的 OpenCV 例程200篇】157. 霍夫变换直线检测
  9. python 物联网服务器_python+树莓派实现IoT(物联网)数据上传到服务器
  10. java中常见对象——StringBuffer
  11. “黑客”深度学习之“游戏外挂原理实现”之美
  12. 编程语言的执行方式、静态语言和脚本语言
  13. Visual studio插件:Visual Svn 替代工具--AnkhSvn
  14. 常用运算放大器 - 选型列表(比较全,参数详细)
  15. 拼多多item_search - 根据关键词取商品列表
  16. cj.exceptions.CJException: Access denied for user XX to database 问题解决记录
  17. 使用微信web开发者工具调试接口数据【开发记录】
  18. php在线考试系统 附源码(一)
  19. 基础-02-日语中为何会有体言用言?
  20. java积分_java积分体系设计

热门文章

  1. python裁剪图片box并保存_python实现图像随机裁剪
  2. C++之static关键字探究
  3. 菜鸟教程java二维数组_asp数组的使用介绍
  4. java输入方法有哪些内容_java 键盘输入多种方法
  5. 工程计算软件_同望BIM工程量计算软件—土石方
  6. iis ajax访问html,为什么Ajax脚本不在IIS 7.5 Win 2008 R2服务器上运行?
  7. 静态成员调用java,Java 反射 静态变量 静态方法 静态成员 调用 获取修饰符 判断是否为静态...
  8. php sesssion,php sesssion原理
  9. ConstantSourceNode
  10. AnalyserNode