题目描述
运用你所掌握的数据结构,设计和实现一个 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
算法1
(python内置:dict + collections.OrderedDict) $O(1)$
一.dict:缓存k/v键值对(但无序)
二.OrderedDict:维护:实现更新最近访问的key
注:
1.OrderedDict是按照key的插入顺序排列(插入越后;就放越后)
2.popitem:last=True(默认)最后一个;last=False第一个
时间复杂度
参考文献
python 代码
class LRUCache:
def __init__(self, capacity: int):
#难点1.OrderedDict()用法:move_to_end函数;del函数;popitem函数
self.od=OrderedDict()
self.capacity=capacity
def get(self, key: int) -> int:#查找
if key in self.od:
val=self.od[key]
self.od.move_to_end(key) #用了就把它放到末尾
return val
else:
return -1
def put(self, key: int, value: int) -> None: #更新k/v
#更新key到表头
if key in self.od:
#记得先删旧的再更新新的
del self.od[key]
#难点2.od写前面(它才是要维护的)
self.od[key]=value
else: #插入
self.od[key]=value
#判断
if len(self.od)>self.capacity:
#last=False 删除第一个
self.od.popitem(last=False)
算法2
(字典+循环双端链表) $O(n^2)$
一.字典:存储节点
二.head+tail:维护双向链表(手写链表)
注:考点
1.add函数:加到链表尾部
2.remove函数:删除指定节点
时间复杂度
参考文献
python 代码
#难点1.创建双向链表
class Node:
def __init__(self, key, val):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
# 构建首尾节点, 使之相连
self.head = Node(0, 0)
self.tail = Node(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
self.lookup = dict()
self.capacity = capacity
def get(self, key: int) -> int:
if key in self.lookup:
node = self.lookup[key]
self.remove(node)
self.add(node)
return node.val
else:
return -1
def put(self, key: int, value: int) -> None:
if key in self.lookup:
self.remove(self.lookup[key])
if len(self.lookup) == self.max_len:
# 把表头位置节点删除(说明最近的数据值)
self.remove(self.head.next)
#难点2.与法一不同,最后add(注意写法Node;add的node.key;配套)
self.add(Node(key, value))
# 删除链表节点
def remove(self, node):
del self.lookup[node.key]
node.prev.next = node.next
node.next.prev = node.prev
#难点3.双向链表加点的写法(指针指向搞清楚)
# 加在链表尾
def add(self, node):
self.lookup[node.key] = node
#先暂存;因为不知道tail的前一个节点
pre_tail = self.tail.prev
node.next = self.tail
self.tail.prev = node
pre_tail.next = node
node.prev = pre_tail