mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-02 13:12:44 +00:00
147 lines
5.7 KiB
Plaintext
147 lines
5.7 KiB
Plaintext
---
|
|
title: '146. LRU Cache'
|
|
description: Design a data structure that follows the constraints of a Least Recently Used (LRU) cache
|
|
icon: dot
|
|
topics:
|
|
- { name: "Hash Table", slug: "hash-table" }
|
|
- { name: "Linked List", slug: "linked-list" }
|
|
- { name: "Design", slug: "design" }
|
|
- { name: "Doubly-Linked List", slug: "doubly-linked-list" }
|
|
video: "7ABFKPK2hD4"
|
|
issue: "https://github.com/prdlk/leetcode/issues/87"
|
|
companies:
|
|
- { name: "NetApp", period: "Thirty Days", frequency: 100 }
|
|
- { name: "Adobe", period: "Thirty Days", frequency: 83.5 }
|
|
- { name: "Bloomberg", period: "Thirty Days", frequency: 69.8 }
|
|
- { name: "Amazon", period: "Thirty Days", frequency: 69.6 }
|
|
- { name: "Meta", period: "Thirty Days", frequency: 53.8 }
|
|
- { name: "Google", period: "Thirty Days", frequency: 36.7 }
|
|
- { name: "TikTok", period: "Three Months", frequency: 100 }
|
|
- { name: "Palo Alto Networks", period: "Three Months", frequency: 92.8 }
|
|
- { name: "Apple", period: "Three Months", frequency: 88.5 }
|
|
- { name: "Goldman Sachs", period: "Three Months", frequency: 66.3 }
|
|
- { name: "Microsoft", period: "Three Months", frequency: 53.2 }
|
|
- { name: "Airwallex", period: "Six Months", frequency: 100 }
|
|
- { name: "Docusign", period: "Six Months", frequency: 100 }
|
|
- { name: "Moloco", period: "Six Months", frequency: 100 }
|
|
- { name: "Nutanix", period: "Six Months", frequency: 100 }
|
|
- { name: "Oracle", period: "Six Months", frequency: 100 }
|
|
- { name: "Twitch", period: "Six Months", frequency: 100 }
|
|
- { name: "Snap", period: "Six Months", frequency: 92.4 }
|
|
- { name: "Qualcomm", period: "Six Months", frequency: 92 }
|
|
- { name: "Nvidia", period: "Six Months", frequency: 91.1 }
|
|
- { name: "ByteDance", period: "Six Months", frequency: 88.6 }
|
|
- { name: "MongoDB", period: "Six Months", frequency: 87.7 }
|
|
- { name: "Expedia", period: "Six Months", frequency: 87.5 }
|
|
- { name: "eBay", period: "Six Months", frequency: 86.1 }
|
|
- { name: "LinkedIn", period: "Six Months", frequency: 74.9 }
|
|
- { name: "Visa", period: "Six Months", frequency: 69.7 }
|
|
- { name: "Uber", period: "Six Months", frequency: 61.9 }
|
|
- { name: "Salesforce", period: "Six Months", frequency: 50.2 }
|
|
sidebar:
|
|
label: 'LRU Cache'
|
|
badge: 'Medium'
|
|
---
|
|
|
|
### Example 1:
|
|
- Input: ``
|
|
- Output: ``
|
|
- Explanation: 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 <= 10^4`
|
|
- `0 <= value <= 10^5`
|
|
- At most 2 * 10^5 calls will be made to get and put.
|
|
|
|
## Approach
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
subgraph state["state — dict for O(1) lookup, doubly linked list for O(1) reorder"]
|
|
H["head — least recent"] --- N["... nodes ..."] --- T["tail — most recent"]
|
|
end
|
|
subgraph get["get(key)"]
|
|
G1{"key in cache?"} -- no --> G2(["return -1"])
|
|
G1 -- yes --> G3["_remove_node then _add_node — move it beside the tail"]
|
|
G3 --> G4(["return node.value"])
|
|
end
|
|
subgraph put["put(key, value)"]
|
|
P1{"key in cache?"} -- yes --> P2["update value, _remove_node then _add_node"]
|
|
P1 -- no --> P3["new Node, cache[key] = node, _add_node"]
|
|
P3 --> P4{"len(cache) > capacity?"}
|
|
P4 -- yes --> P5["evict head.next — the least recent — and del cache[lru.key]"]
|
|
P4 -- no --> P6(["done"])
|
|
P2 --> P6
|
|
P5 --> P6
|
|
end
|
|
state --> get
|
|
state --> put
|
|
```
|
|
|
|
## Solution
|
|
|
|
```py
|
|
class Node:
|
|
def __init__(self, key=0, value=0):
|
|
self.key = key
|
|
self.value = value
|
|
self.prev = None
|
|
self.next = None
|
|
|
|
|
|
class LRUCache:
|
|
def __init__(self, capacity: int):
|
|
self.capacity = capacity
|
|
self.cache = {}
|
|
self.head = Node()
|
|
self.tail = Node()
|
|
self.head.next = self.tail # pyright: ignore[reportAttributeAccessIssue]
|
|
self.tail.prev = self.head # pyright: ignore[reportAttributeAccessIssue]
|
|
|
|
def _remove_node(self, node):
|
|
prev_node = node.prev
|
|
next_node = node.next
|
|
prev_node.next = next_node
|
|
next_node.prev = prev_node
|
|
|
|
def _add_node(self, node):
|
|
# Insert just before the tail (most recent at end)
|
|
node.next = self.tail
|
|
node.prev = self.tail.prev
|
|
self.tail.prev.next = node # pyright: ignore[reportAttributeAccessIssue]
|
|
self.tail.prev = node
|
|
|
|
def get(self, key: int) -> int:
|
|
if key not in self.cache:
|
|
return -1
|
|
|
|
node = self.cache[key]
|
|
self._remove_node(node)
|
|
self._add_node(node)
|
|
return node.value
|
|
|
|
def put(self, key: int, value: int) -> None:
|
|
if key in self.cache:
|
|
node = self.cache[key]
|
|
node.value = value
|
|
self._remove_node(node)
|
|
self._add_node(node)
|
|
else:
|
|
new_node = Node(key, value)
|
|
self.cache[key] = new_node
|
|
self._add_node(new_node)
|
|
|
|
if len(self.cache) > self.capacity:
|
|
lru = self.head.next # Oldest node is next to the head
|
|
self._remove_node(lru)
|
|
del self.cache[lru.key] # pyright: ignore[reportAttributeAccessIssue]
|
|
|
|
|
|
# Your LRUCache object will be instantiated and called as such:
|
|
# obj = LRUCache(capacity)
|
|
# param_1 = obj.get(key)
|
|
# obj.put(key,value)
|
|
```
|