LeetCode题解
第146题 LRU缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
1 2 3 4 5 6 7 8 9 10 11
| LRUCache cache = new LRUCache( 2 );
cache.put(1, 1); cache.put(2, 2); cache.get(1); cache.put(3, 3); cache.get(2); cache.put(4, 4); cache.get(1); cache.get(3); cache.get(4);
|
原题如上,代码如下:
python版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| class new_node: def __init__(self,key=0,value=0): self.key = key self.value = value self.prev = None self.next = None
class LRUCache(object): def add_none(self,node): node.prev = self.head node.next = self.head.next self.head.next.prev = node self.head.next = node def remove_node(self,node): prev = node.prev new = node.next prev.next = new new.prev = prev def move_to_head(self,node): self.remove_node(node) self.add_none(node) def remove_last_node(self): last = self.tail.prev self.remove_node(last) return last
def __init__(self, capacity): """ :type capacity: int """ self.cache = {} self.size = 0 self.capacity = capacity self.head = new_node() self.tail = new_node() self.head.next = self.tail self.tail.prev = self.head
def get(self, key): """ :type key: int :rtype: int """ node = self.cache.get(key) if not node: return -1
self.move_to_head(node) return node.value
def put(self, key, value): """ :type key: int :type value: int :rtype: None """ node = self.cache.get(key) if not node: newnode = new_node(key,value) self.cache[key] = newnode self.add_none(newnode) self.size += 1 if self.size > self.capacity: tail = self.remove_last_node() del self.cache[tail.key] self.size -= 1 else: node.value = value self.move_to_head(node)
|
解析:
首先,我们看到这个题所要构建的数据结构要求要在常数时间内完成get和put操作,那么可以用到哈希表,也就是python中的字典结构,辅助双向链表记录key-value信息,对于双向链表加入head和tail两个哨兵方便使用。
整体结构为:
我们先构造链表结构:
1 2 3 4 5 6
| class new_node: def __init__(self,key=0,value=0): self.key = key self.value = value self.prev = None self.next = None
|
对于字典结构key值是数据结构中的key,而字典中的value是链表中的结构体Node。
在我们构造LRUcache之前,我们先得构造几种方法,并结合要求使用。
首先,LRU是最近最久未使用,我们可以用双向链表进行对使用时间进行有序排序,即最近到最久的顺序。链表中最后一个,也就是最久没有使用的那一个。
对双向链表进行维护时,会有多种情况:
put一个key相同的key-value,我们不用管它们的value是否相同,对我们而言是要原来的key-value进行更新,我们要将原来的key-value更新后将其位置移动到第一个。这时候无论cache是否满的,都不会溢出,因为只是更新处理而已。
put一个新的key-value,我们会构建一个新的key-value结构体,并加入到链表中放到第一个位置,在字典中也加入新的key-value,然后判断是否溢出缓存,若溢出,那么将最久未使用也就是链表中最后一个元素删除,同时删除字典中该元素的key-value。
上述中我们发现我们还需要:
在链表中新加Node并放置在第一个位置:
1 2 3 4 5
| def add_none(self,node): node.prev = self.head node.next = self.head.next self.head.next.prev = node self.head.next = node
|
移除old_Node:
1 2 3 4 5
| def remove_node(self,node): prev = node.prev new = node.next prev.next = new new.prev = prev
|
将old_Node移动到第一个位置:
1 2 3
| def move_to_head(self,node): self.remove_node(node) self.add_none(node)
|
将最后一个Node删除:
1 2 3 4
| def remove_last_node(self): last = self.tail.prev self.remove_node(last) return last
|
返回Node是为了使用它在字典中将它删除。
get一个key,查询其value,若没有,返回-1,若有,返回其value,并刷新使用时间,也就是将其key-value移动到链表第一个位置。
由上述就可以构造put和get方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| def put(self, key, value): """ :type key: int :type value: int :rtype: None """ node = self.cache.get(key) if not node: newnode = new_node(key,value) self.cache[key] = newnode self.add_none(newnode) self.size += 1 if self.size > self.capacity: tail = self.remove_last_node() del self.cache[tail.key] self.size -= 1 else: node.value = value self.move_to_head(node)
|
1 2 3 4 5 6 7 8 9 10 11
| def get(self, key): """ :type key: int :rtype: int """ node = self.cache.get(key) if not node: return -1
self.move_to_head(node) return node.value
|
同时对LRUcache的基本属性为:
1 2 3 4 5 6 7 8 9 10 11
| def __init__(self, capacity): """ :type capacity: int """ self.cache = {} self.size = 0 self.capacity = capacity self.head = new_node() self.tail = new_node() self.head.next = self.tail self.tail.prev = self.head
|