题目来源:力扣(LeetCode)和牛客网在线编程题
链接:https://leetcode-cn.com/problems
https://www.nowcoder.com/activity/oj
特别鸣谢:来自夸夸群的 醉笑陪公看落花@知乎,王不懂不懂@知乎,QFIUNE@csdn
感谢任老师倾囊相授,感谢小伙伴们督促学习,一起进步
文章目录
难度中等
面试题 16.25. LRU 缓存/146. LRU 缓存机制
二者是同一道题
设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。
它应该支持以下操作: 获取数据 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lru-cache-lcci
解题思路tips:
- 双向循环链表
- 头插法
- 记录头尾结点
- 控制链表长度 count 和 capacity
- 每次访问(put or get)之后, 在原队列上删除,然后再把这个节点插入头部
- 每次 put之后, 键已经存在的情况下,需要更新值
- 链表达到最长长度时,删除尾节点的pre
- 字典, 通过键快速访问链表中的节点
- 节点里面需要存key,删除尾节点的pre的时候,可以查询到key值,从而把这个key从字典里面删掉
- 访问的节点 == header.next , 不用在队列上删除了再插入
解题代码
# Title : lc16_25_LRU缓存.py
# Created on: 2021/7/16 22:51
class Node:
def __init__(self,k,v):
self.k = k
self.v = v
self.next = None
self.pre = None
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.containers = {}
self.count = 0
self.header = Node(None,0)
self.tail = Node(None,0)
self.header.next = self.tail
self.tail.pre = self.header
def get(self, key: int) -> int:
if key in self.containers.keys():
node = self.containers[key]
if self.header.next == node:return self.containers[key].v
self.delete(node)
node = self.insert(node)
self.containers[key] = node
return self.containers[key].v
else:return -1
def put(self, key: int, value: int) -> None:
node = Node(key, value)
if key in self.containers.keys():
old_node = self.containers[key]
self.delete(old_node)
node = self.insert(node)
else:
if self.count <self.capacity:
self.count += 1
else:
# delete node near tail
delete_node = self.delete(self.tail.pre)
self.containers.pop(delete_node.k)
node = self.insert(node)
self.containers[key] = node
pass
def delete (self,node:Node):
node.pre.next = node.next
node.next.pre = node.pre
return node
def insert(self,node:Node):
if not self.header.next: # empyty
self.header.next = node
node.pre = self.header
else:
node.next = self.header.next
node.pre = self.header
self.header.next = node
node.next.pre = node
return node
#
if __name__ == '__main__':
opr = ["LRUCache", "put", "put", "get", "put", "put", "get"]
data = [[2], [2, 1], [2, 2], [2], [1, 1], [4, 1], [2]]
# [null,null,null,2,null,null,-1]
capacity = data[0][0]
obj = LRUCache(capacity)
print(None,end=',')
for idx in range(1,len(data)):
if opr[idx] == 'put': print(obj.put(data[idx][0],data[idx][1]),end=',')
else:
print(obj.get(data[idx][0]),end=',')
牛客网 LRU缓存 解题代码
设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
set(key, value):将记录(key, value)插入该结构
get(key):返回key对应的value值
[要求]
set和get方法的时间复杂度为O(1)
某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案
输入:[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
返回值:[1,-1]
https://www.nowcoder.com/practice/e3769a5f49894d49b871c09cadd13a61
同上题,只是需要手动处理一下输入信息,修改一下入口参数即可
# Title : lc16_25_LRU缓存.py
# Created on: 2021/7/16 22:51
#
# lru design
# @param operators int整型二维数组 the ops
# @param k int整型 the k
# @return int整型一维数组
#
class Node:
def __init__(self, k, v):
self.k = k
self.v = v
self.next = None
self.pre = None
class Solution:
def __init__(self):
self.capacity = None
self.containers = {}
self.count = 0
self.header = Node(None, 0)
self.tail = Node(None, 0)
self.header.next = self.tail
self.tail.pre = self.header
def LRU(self, operators, k):
self.capacity = k
# write code here
ans = []
for opr in operators:
if opr[0] == 1:
self.set(opr[1], opr[2])
else:
ans.append(self.get(opr[1]))
return ans
def get(self, key: int) -> int:
if key in self.containers.keys():
node = self.containers[key]
if self.header.next == node: return self.containers[key].v
self.delete(node)
node = self.insert(node)
self.containers[key] = node
return self.containers[key].v
else:
return -1
def set(self, key: int, value: int) -> None:
node = Node(key, value)
if key in self.containers.keys():
old_node = self.containers[key]
self.delete(old_node)
node = self.insert(node)
else:
if self.count < self.capacity:
self.count += 1
else:
# delete node near tail
delete_node = self.delete(self.tail.pre)
self.containers.pop(delete_node.k)
node = self.insert(node)
self.containers[key] = node
def delete(self, node: Node):
node.pre.next = node.next
node.next.pre = node.pre
return node
def insert(self, node: Node):
node.next = self.header.next
node.pre = self.header
self.header.next = node
node.next.pre = node
return node
#
if __name__ == '__main__':
# 输入:
# [[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
# 返回值:
# [1,-1]
operators = [[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]]
k = 3
ans = Solution().LRU(operators, k)
print(ans)
解题草稿
缓存机制在递归函数中的应用
在leetcode 32题中,零钱凑整中
带备忘录的递归代码
class Solution:
mydict = {}
def coinChange(self, coins,amount):
self.coins = coins
self.mydict = {} # 备忘录,记住算过的金额
return self.dp(amount)
def dp(self,amount):
if amount == 0:
return 0
elif amount < 0:
return -1
if amount in self.mydict:return self.mydict[amount]
min_tp = sys.maxsize
for c in self.coins:
tp = self.dp(amount - c) + 1
if (tp > 0): min_tp = min(tp, min_tp)
ans = -1 if min_tp == sys.maxsize else min_tp
self.mydict[amount] = ans # # 用备忘录记录
return ans
用functools.lru_cache()记录缓存 代码
import functools
import sys
class Solution:
def coinChange(self, coins,amount):
self.coins = coins
@functools.lru_cache() # 加上缓存之后,就消除了递归中的重复计算
def dp(amount):
if amount==0:return 0
elif amount<0:return -1
min_tp = sys.maxsize
for c in coins:
tp = dp(amount-c)+1
if (tp>0):min_tp=min(tp,min_tp)
return -1 if min_tp==sys.maxsize else min_tp
return dp(amount)