メインコンテンツへスキップ

NeetCode 150 [Linked List]medium:LRU Cache

· loading · loading ·
kiitosu
著者
kiitosu
画像処理やデバイスドライバ、データ基盤構築からWebバックエンドまで、多様な領域に携わってきました。地図解析や地図アプリケーションの仕組みにも経験があり、幅広い技術を活かした開発に取り組んでいます。休日は草野球とランニングを楽しんでいます。
目次

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がある場合はそのkeyvalueを更新します。存在しない場合はkey-valueのペアをキャッシュに追加します。
  • 新しいペア導入時にキャッシュサイズを超える場合、最も最後に使ったキーのペアを削除します
  • getあるいはputが呼び出されたらそのkeyは使われたとします

制約
#

計算量:getputは平均で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]
Reply by Email

関連記事

NeetCode 150 [Linked List]medium:Find the Duplicate Number
· loading · loading
NeetCode 150 [Linked List] medium : Add Two Numbers
· loading · loading
NeetCode 150 [Linked List] medium : Copy List With Random Pointer
· loading · loading