LRU缓存机制
LRU缓存机制有几个特点:
- 缓存cache是带有容量的
- 缓存必须有顺序之分,以区分最近使用和久未使用的数据(读数据也算使用)
- 读和写数据都比如快,时间复杂度为O(1)
为达到上述目的,LRU缓存算法的核心数据结构就是哈希链表,即双向链表和哈希表的结合。双向链表维护的是使用数据的顺序关系,哈希表方便读写操作。
# 双向链表
class ListNode:
def __init__(self, key=None, value=None):
self.key = key
self.value = value
self.prev = None
self.next = None
# LRU缓存
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.hashmap = {}
self.head = ListNode()
self.tail = ListNode()
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key):
pass
def put(self, key, value):
pass
这样,基础的框架就有了,现在来实现lru的两个api,get和put,在定义这两个方法之前,需要弄清楚,首先,无论是get还是put,都需要更新一次双向链表,主要就是把操作的元素移到链表头,其次,再put方法之后,如果链表的长度超过了容量,就需要删除链表尾的元素。因此,整个代码结构可以写为:
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.hashmap = {}
self.head = ListNode()
self.tail = ListNode()
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key: int) -> int:
# 如果key在链表中的话,就把对应的node移动到链表头
if key in self.hashmap:
self.move_node_to_header(key)
res = self.hashmap.get(key, -1)
if res == -1:
return res
else:
return res.value
def put(self, key: int, value: int) -> None:
# 如果key在链表中的话,就把对应的node移动到链表头
if key in self.hashmap:
self.hashmap[key].value = value
self.move_node_to_header(key)
# 如果key不在链表中的话,就先检查容量,删除链表尾元素,再在链表头添加新的node
else:
if len(self.hashmap) >= self.capacity:
self.pop_tail()
self.add_node_to_header(key,value)
def move_node_to_header(self, key):
node = self.hashmap[key]
node.prev.next = node.next
node.next.prev = node.prev
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def add_node_to_header(self, key,value):
new = ListNode(key, value)
self.hashmap[key] = new
new.prev = self.head
new.next = self.head.next
self.head.next.prev = new
self.head.next = new
def pop_tail(self):
last_node = self.tail.prev
self.hashmap.pop(last_node.key)
last_node.prev.next = self.tail
self.tail.prev = last_node.prev
return last_node
LRU缓存的设计方法有很多中,这里只是写出了一种通用的设计方法,其他方式诸如利用双字典和单列表储存索引,或者利用OrderDict构建双向链表和hash表的接合体,只要操作时间复杂度都是O(1),都是满足要求的。