🐙
NeetCode 150 [Linked List]medium:LRU Cache
NeetCodeのSolutionを書いていく
LRU Cache
問題
Least Recently Used Cacheを実装しましょう。
以下の操作をサポートする必要があります。
-
LRUCache(int capacity)
でLRUのキャッシュサイズを初期化します -
int get(int key)
でkeyがある場合はその値を、ない場合は-1を返します -
void put(int key, int value)
でkey
がある場合はそのkey
のvalue
を更新します。存在しない場合はkey
-value
のペアをキャッシュに追加します。 - 新しいペア導入時にキャッシュサイズを超える場合、最も最後に使ったキーのペアを削除します
-
get
あるいはput
が呼び出されたらそのkeyは使われたとします
制約
計算量:get
、put
は平均でO(1)
メモリ:O(n)
例
Input:
["LRUCache", [2], "put", [1, 10], "get", [1], "put", [2, 20], "put", [3, 30], "get", [2], "get", [1]]
Output:
[null, null, 10, null, null, 20, -1]
Explanation:
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 10); // cache: {1=10}
lRUCache.get(1); // return 10
lRUCache.put(2, 20); // cache: {1=10, 2=20}
lRUCache.put(3, 30); // cache: {2=20, 3=30}, key=1 was evicted
lRUCache.get(2); // returns 20
lRUCache.get(1); // return -1 (not found)
メモ
keyはユニークなのでDictが使える。
DuctだとO(1)でget,putできる。
しかしDuctだと順番が管理しづらいか?
Listにkeyを保持する形だと、keyの削除でO(n)になってしまうのでだめ。
以下方針だとどうだろうか。
- key-valueはDictで管理する
- keyを管理する双方向リスト(cache list)を実装
- putされたらkeyをcache listの末尾に追加
- putしたときにキャッシュサイズを超えたら最初のデータを削除
- getされたらkeyをcache listの末尾に移動
キャッシュのheadとtailを持っておく
メモリがO(n)使えるのでkeyをキーに、LinkedListのnodeを保持しておく
class ListNode:
def __init__(self, key=0, val=0):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.cache: dict[int, ListNode] = {}
self.head = ListNode() # ダミーhead
self.tail = ListNode() # ダミーtail
self.head.next = self.tail
self.tail.prev = self.head
def _remove(self, node):
# ノードをリストから外す
prev, nxt = node.prev, node.next
prev.next = nxt
nxt.prev = prev
def _add(self, node):
# ノードを末尾に追加(tailの前)
prev = self.tail.prev
prev.next = node
node.prev = prev
node.next = self.tail
self.tail.prev = node
def get(self, key: int) -> int:
if key in self.cache:
node = self.cache[key]
self._remove(node)
self._add(node)
return node.val
return -1
def put(self, key: int, value: int) -> None:
if key in self.cache:
self._remove(self.cache[key])
node = ListNode(key, value)
self._add(node)
self.cache[key] = node
if len(self.cache) > self.capacity:
# head.next が一番古いノード
lru = self.head.next
self._remove(lru)
del self.cache[lru.key]
Discussion