Fix the Bug in the Optimized LRU Cache Implementation
Identify and fix the subtle bug in the provided Least Recently Used (LRU) cache implementation which causes it to fail under certain eviction conditions. The solution must maintain O(1) time complexity for get and put operations while correctly managing the cache size and order.
Challenge prompt
You are given a Python class implementing an LRU cache using a dictionary and a doubly linked list. The goal is to store key-value pairs with constant time retrieval and eviction based on the least recently used policy. However, the current implementation has a bug that results in incorrect eviction behavior once the cache reaches its capacity. Your task is to identify and fix the bug so that the cache evicts the correct item when full and maintains the correct internal state.
Guidance
- • Pay close attention to the eviction logic when the cache size exceeds capacity.
- • Examine how the doubly linked list nodes are updated during get and put operations.
- • Verify that all pointers (prev and next) are correctly set during insertions and removals.
Hints
- • Check if the tail node is always correctly identified before removal.
- • Look for any off-by-one errors or incorrect pointer assignments in the eviction process.
- • Consider if the cache size is updated appropriately after node removal.
Starter code
class Node:
def __init__(self, key, val):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = dict() # key -> node
self.head = Node(0, 0)
self.tail = Node(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
def _remove(self, node):
prev = node.prev
next = node.next
prev.next = next
next.prev = prev
def _add(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def get(self, key):
if key in self.cache:
node = self.cache[key]
self._remove(node)
self._add(node)
return node.val
return -1
def put(self, key, value):
if key in self.cache:
self._remove(self.cache[key])
node = Node(key, value)
self._add(node)
self.cache[key] = node
if len(self.cache) > self.capacity:
node_to_remove = self.tail.prev
self._remove(node_to_remove)
# Bug here: missing deletion from cache dictionary
Expected output
After fixing, the code should pass these operations: lru = LRUCache(2) lru.put(1, 1) # cache is {1=1} lru.put(2, 2) # cache is {1=1, 2=2} lru.get(1) # returns 1 lru.put(3, 3) # evicts key 2, cache is {1=1, 3=3} lru.get(2) # returns -1 (not found) lru.put(4, 4) # evicts key 1, cache is {4=4, 3=3} lru.get(1) # returns -1 (not found) lru.get(3) # returns 3 lru.get(4) # returns 4
Core concepts
Challenge a Friend
Send this duel to someone else and see if they can solve it.