如何在O(1)时间复杂度下实现LRU
时间:2022-07-23
本文章向大家介绍如何在O(1)时间复杂度下实现LRU,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一、题意分析
通常我们会把频繁用到的数据放到缓存里,这样取数据比较快,但内存有限,所以经常会有一些淘汰策略,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]
- 【Go 语言社区】在golang里实现类似try catch 的异常处理机制
- 每秒执行6000的简单SQL优化(二) (r10笔记第65天)
- 【Go 语言社区】GO语言练习:网络编程 ICMP 示例
- Golang的json操作
- 【Go 语言社区】golang协程——通道channel阻塞
- Oracle 12c PDB中碰到的DG问题 (r10笔记第63天)
- 上线必备 | 高性能ES5.X部署配置清单
- 【Go 语言社区】go 学习中遇到一些语法问题
- Elasticsearch全文检索实战小结——复盘我带的第二个项目
- 当12C PDB遇上JDBC (r10笔记第59天)
- 微信公众平台开发获取用户基本信息--转
- Elasticsearch检索分类深入详解—基础篇
- 通过Java程序测试数据库连接信息 (r10笔记第64天)
- GoldenGate安装简记(r10笔记第78天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python语言中有算法吗
- jQuery ajax+PHP实现的级联下拉列表框功能示例
- PHP+mysql防止SQL注入的方法小结
- 浅谈tensorflow 中的图片读取和裁剪方式
- Python实现SMTP邮件发送
- 浅谈多卡服务器下隐藏部分 GPU 和 TensorFlow 的显存使用设置
- 通过PHP设置BugFree获取邮箱通知
- Django封装交互接口代码
- 使用K.function()调试keras操作
- tensorflow图像裁剪进行数据增强操作
- ThinkPHP3.2.3框架Memcache缓存使用方法实例总结
- Python+PyQt5+MySQL实现天气管理系统
- 浅谈Python协程
- Python logging模块异步线程写日志实现过程解析
- php-fpm重启导致的程序执行中断问题详解