पाठ 41 / 42
LRU Cache Design
O(1) get/put और least-recently-used eviction के लिए hash map को doubly linked list के साथ मिलाएँ।
दो structures, एक काम
LRU cache को O(1) get, O(1) put, और least-recently-used item का O(1) eviction चाहिए। अकेला hash map तेज़ lookup देता है पर कोई क्रम नहीं; अकेली linked list क्रम देती है पर धीमी lookup। दोनों मिलाने से दोनों मिलते हैं।
Doubly linked list + hash map
Hash map key -> node को O(1) lookup के लिए रखता है। Doubly linked list nodes को उपयोग-क्रम में रखती है: access पर node को आगे (सबसे हाल का) ले जाएँ, और भरने पर पीछे (सबसे पुराना) से हटाएँ।
class Node:
def __init__(self, key, val):
self.key, self.val = key, val
self.prev = self.next = None
class LRUCache:
def __init__(self, capacity):
self.cap = capacity
self.map = {}
self.head = Node(0, 0) # dummy most-recent end
self.tail = Node(0, 0) # dummy least-recent end
self.head.next, self.tail.prev = self.tail, self.head
def _remove(self, node):
node.prev.next, node.next.prev = node.next, node.prev
def _add_front(self, node):
node.next, node.prev = self.head.next, self.head
self.head.next.prev = node
self.head.next = node
def get(self, key):
if key not in self.map:
return -1
node = self.map[key]
self._remove(node)
self._add_front(node)
return node.val
def put(self, key, val):
if key in self.map:
self._remove(self.map[key])
node = Node(key, val)
self.map[key] = node
self._add_front(node)
if len(self.map) > self.cap:
lru = self.tail.prev
self._remove(lru)
del self.map[lru.key]Python का शॉर्टकट
Interviews में इसे हाथ से बनाया जाता है, पर असली Python code में collections.OrderedDict यह पहले से करता है — access पर move_to_end() और सबसे पुराना हटाने के लिए popitem(last=False)।
त्वरित जांच
Design की समझ परखें।
त्वरित जाँच: LRU cache को singly linked list के बजाय doubly linked list की ज़रूरत क्यों है?
- यह कम memory इस्तेमाल करती है
- यह किसी node को केवल उसका pointer देकर O(1) में हटाने देती है
- यह keys को वर्णानुक्रम में क्रमबद्ध करती है
- Hash maps को doubly linked lists चाहिए ही होती हैं
Answer
यह किसी node को केवल उसका pointer देकर O(1) में हटाने देती है — prev pointer के साथ, एक node खुद को O(1) में unlink कर सकता है; singly linked list में पिछला node खोजने में O(n) लगेगा।