Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
Implement the LRUCache
class:
LRUCache(int capacity)
Initialize the LRU cache with positive sizecapacity
.int get(int key)
Return the value of thekey
if the key exists, otherwise return-1
.void put(int key, int value)
Update the value of thekey
if thekey
exists. Otherwise, add thekey-value
pair to the cache. If the number of keys exceeds thecapacity
from this operation, evict the least recently used key.
The functions get
and put
must each run in O(1)
average time complexity.
Example 1:
Input ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] Output [null, null, null, 1, null, -1, null, -1, 3, 4] Explanation LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, 1); // cache is {1=1} lRUCache.put(2, 2); // cache is {1=1, 2=2} lRUCache.get(1); // return 1 lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3} lRUCache.get(2); // returns -1 (not found) lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3} lRUCache.get(1); // return -1 (not found) lRUCache.get(3); // return 3 lRUCache.get(4); // return 4
Constraints:
1 <= capacity <= 3000
0 <= key <= 104
0 <= value <= 105
- At most 2
* 105
calls will be made toget
andput
.
题目要求设计一个LRU(Least Recently Used) Cache,中文叫最近最少使用缓存。做这题之前可以找一本操作系统的书先了解一下LRU。其核心思想就是当缓存满了的时候,插入新内容之前需要先丢弃最近最少使用的那个单元的内容(即缓存里最长时间没被读或写的那个单元)。如果某一个单元刚被读或写过,那么该单元就变成了缓存里最近使用过的(Most Recently Used)。
要求实现如下接口函数:
LRUCache(int capacity)
初始化缓存大小,以及实现LRU所需的数据结构和变量。int get(int key)
给定一个key,如存在返回对应的value,如不存在返回-1,该key-value所在的单元变成最近使用过的(Most Recently Used)void put(int key, int value)
更新key对应的value,如key已存在则更新value;如key不存在则插入一个key-value新单元,要是缓存已满则先把最近最少使用least recently used的key-value丢弃。刚被更新过的或者新插入的key-value单元就变成最近使用过的Most Recently Used。
要求 get()和put()的时间复杂度都O(1)。
根据题目分析双向链表是最合适的数据结构。每个节点存储一对key-value,最近使用的在链表头,最近最少使用的在链表尾。对于一个新节点则直接插入到链表头因为它本身就是the most recently used。对于链表里已存在的任意一个key,只要它对应的节点被读或写之后就变成了the most recently used,因此需要从当前位置删除重新插入到链表头。另外,为了在O(1)时间内得到key对应的节点,需要借助一个字典dict: 从key映射到节点node。
class ListNode:
def __init__(self, key = None, val = None):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.head = ListNode()
self.tail = ListNode()
self.head.next = self.tail
self.tail.prev = self.head
self.cnt = 0
self.size = capacity
self.v2n = {}
def get(self, key: int) -> int:
if key not in self.v2n:
return -1
node = self.v2n[key]
self.delete(node)
self.insert(node)
return node.val
def put(self, key: int, value: int) -> None:
node = None
if key in self.v2n:
node = self.v2n[key]
node.val = value
self.delete(node)
else:
node = ListNode(key, value)
if self.cnt == self.size:
self.v2n.pop(self.tail.prev.key)
self.delete(self.tail.prev)
self.cnt -= 1
self.cnt += 1
self.v2n[key] = node
self.insert(node)
def insert(self, node):
self.head.next.prev = node
node.next = self.head.next
node.prev = self.head
self.head.next = node
def delete(self, node):
node.next.prev = node.prev
node.prev.next = node.next