程序员面试金典面试题16。25。LRU缓存
题目难度: 中等
原题链接 [1]
今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述
设计和构建一个"最近最少使用"缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。
它应该支持以下操作: 获取数据 get 和 写入数据 put 。 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 示例:LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); cache.put(1, 1); cache.put(2, 2); cache.get(1); // 返回 1 cache.put(3, 3); // 该操作会使得密钥 2 作废 cache.get(2); // 返回 -1 (未找到) cache.put(4, 4); // 该操作会使得密钥 1 作废 cache.get(1); // 返回 -1 (未找到) cache.get(3); // 返回 3 cache.get(4); // 返回 4 题目思考相当经典的一道题目, 要使得更新和查询都是 O(1)复杂度, 你想到了哪些数据结构组合能做到? 解决方案思路先设计需要使用的数据结构 根据 LRU 描述, 我们至少需要一个数据结构来存节点的新旧程度, 新的可以放一边, 老的放另一边, 这自然就是双向链表但是问题是链表虽然更新很快, 是 O(1), 但如何快速通过一个 key 查到对应的节点呢?很容易想到字典/hash 可以做到 O(1)查询, 然后要做的就是把这两个数据结构结合起来: 双向链表存节点的先后顺序, head 最新, tail 最老字典存 key=>节点的映射, 方便快速根据 key 定位到节点 接下来就是写具体的逻辑了, 这里一共有 3 种操作: get 和 put 已经存在的节点: 把已经存在的节点重新放在头部put 新的节点: 把新的节点放在头部删除最老的节点: 如果加入新节点后超过 capacity, 那么需要把最老的 tail 给去掉 逻辑优化 根据第 2 步中分析的 3 种操作, 我们可以直接写出每一种操作对应的代码, 但是里面会有很多重复的部分, 比如放在头部的操作在 2.1 和 2.2 都有, 而删除节点的操作则在 2.1 和 2.3 都有所以我们完全可以将这两部分操作提取出来一个是 add 节点操作, 把节点放到头部, 并更新连接关系和字典一个是 remove 节点操作, 删除某个节点, 并更新连接关系和字典 最后就是具体的代码部分了, 下面代码对每步操作都有详细的注释, 希望可以帮助大家更好理解 复杂度时间复杂度 O(1): 链表保证更新是 O(1), 字典保证查询是 O(1) 空间复杂度 O(C): C 是 capacity, 字典需要存这么多个 kv, 所以是 O(C) 代码class LRUCache: class BiNode: def __init__(self, k, v): self.key = k self.val = v self.pre = None self.nex = None def __init__(self, capacity: int): # 双向链表+字典 # 双向链表存节点的先后顺序, head最新, tail最老 # 字典方便快速根据key定位到节点 # 注意链表节点需要存key和value, 存key的目的是用于字典的检索 # 两者结合就能保证更新和查询的时间复杂度都是O(1), 链表保证更新是O(1), 字典保证查询是O(1) # 注意提取新增和移除节点的逻辑, 方便复用, 简化代码 self.head = None self.tail = None self.kv = {} self.capacity = capacity def add(self, node): # 将节点加到链表头 # 先操作字典 self.kv[node.key] = node # 再更新节点连接关系和头尾 if not self.head: # 没有head, 说明当前链表为空, 直接head和tail都设为node即可 self.head = self.tail = node else: # 更新新的head以及它与老head的连接关系 node.nex = self.head self.head.pre = node self.head = node def remove(self, node): # 移除某个节点 # 先操作字典 if node.key in self.kv: del self.kv[node.key] # 再更新节点连接关系和头尾 # 更新左右邻居的连接关系 pre, nex = node.pre, node.nex # 注意当前节点的pre和nex都要重置为None node.pre = node.nex = None if pre: pre.nex = nex if nex: nex.pre = pre # 更新新的头尾 if node == self.head: self.head = nex if node == self.tail: self.tail = pre def get(self, key: int) -> int: # 注意get操作也需要将node更新到链表头 if key in self.kv: node = self.kv[key] # 先把node从当前位置移除, 然后加入头部 self.remove(node) self.add(node) return node.val return -1 def put(self, key: int, value: int) -> None: if self.capacity <= 0: # 如果capacity是0, 直接无法添加 return if key in self.kv: # 如果当前key存在的话, 先移除它 self.remove(self.kv[key]) elif len(self.kv) == self.capacity: # 注意如果当前已经达到capacity的话先移除tail self.remove(self.tail) # 加入新节点到头部 newNode = self.BiNode(key, value) self.add(newNode) 参考资料
[1]
原题链接: https://leetcode-cn.com/problems/lru-cache-lcci/
win10专业版玩游戏花屏的具体解决方法有不少深度技术的小伙伴,都喜欢玩游戏的,但是一位安装win10专业版的用户却遇到玩游戏花屏的问题,也许有不少用户都遇到过这个问题吧,下面深度系统小编来分享一下相关的解决方法。方法如
windows7玩魔兽争霸3无法进入闪退修复的方法有深度技术windows7中文版系统用户,喜欢在电脑中玩魔兽争霸3的游戏,可是安装好游戏以后却发现无法进入游戏,一直卡在装载中,或者出现闪退的问题,为此,深度系统小编给大家分享一下
已经死去的KTV上一次去KTV是什么时候?当朋友问起的时候,我的大脑使劲搜索了一下回忆,发现上一次真正意义上去KTV还是大学毕业前班里同学聚会。那时候正值乐坛快速发展的时候,男有周王林陶,女有孙莫
14岁奥运冠军全红婵爆红后,最不愿看到的一幕还是发生了作者在风来源在风快跑(IDcrazyingstory)这几天,14岁的全红婵,刷屏全网。这个女孩到底有多牛逼呢?她在决赛中跳了五次,其中三次拿了满分,当场打破了世界记录!这个瞬间被
霍尊官宣退圈,父亲火风力证儿子清白来源文翼说(IDwenyishuo66)古风男神霍尊和舞蹈演员陈露的事,已经发酵好几天了。8日,陈露晒出自己和霍尊的亲密照并霍尊,疑似公开两人的情侣关系,却未等到霍尊本人回应。10
恶魔在枕边死的是我老婆啊,得多骗点钱当我们在说一个人不择手段的时候,很多时候他并不是不择手段,只是花样比较多,在规则的边缘反复试探。真正的不择手段是骗保者为了金钱,可以去杀害自己的亲人,并且内心毫无愧疚。在他们眼里,
乔家的儿女热播为什么好人,却都难有好婚姻?公众号关注诺好杂谈了解人生百态最近口碑爆表的剧,非乔家的儿女莫属。(图片来自网络侵删)乔家的几个孩子,从很小的时候就丧母,而父亲乔祖望却并没有像一个称职的父亲的那样,担负起儿女的抚
后真相时代的我们,理性看待西安地铁拖拽事件公众号关注诺好杂谈了解人生百态前几天西安地铁保安拖拽女乘客事件引发舆论热议。如今热度散去,我们再回过头看这件事,是否会不一样呢?时间退回到8月30日,在西安地铁3号线上,一名女子与
重提冠姓权女性与姓氏的争端公众号关注诺好杂谈了解人生百态大概一年前,某天我和母亲走在路上,遇到一个不怎么熟悉的人问路,那人顺口问了一句你姓什么?母亲说姓陈。这件事让我大发雷霆,因为,陈是父亲的姓氏。写到这里
流畅运行PS,还可以玩英雄联盟惠普星14笔记本体验让你用一个词去形容惠普星14这款笔记本电脑,你会用什么样的词呢?如果让我来选,我可能会用灵动去形容它。我们在选笔记本时可能会遇到这样的问题,选性能强悍的游戏本,大多太过于笨重不能随
电脑文件太多放不下?这款希捷新睿翼移动硬盘值得推荐我为什么要入手希捷新睿翼因为要经常外出码字,所以前段时间入手了MacbookAir,这款笔记本性能满足日常需要而且还轻巧,整体我比较满意。但作为一个经常要剪辑视频的工作者,使用Ma