11class Node :
2+ __slots__ = ("key" , "value" , "next" , "previous" )
3+
24 # Create a building block for the cache
35 def __init__ (self , key , value ):
46 self .key = key
@@ -7,68 +9,86 @@ def __init__(self, key, value):
79 self .previous = None
810
911
12+ class DoublyLinkedList :
13+ # Manages node ordering (head, tail, pushing, removing, and moving to head)
14+ def __init__ (self ):
15+ self .head = None
16+ self .tail = None
17+
18+ def push_head (self , node ):
19+ if self .head is None :
20+ self .head = node
21+ self .tail = node
22+ else :
23+ node .next = self .head
24+ self .head .previous = node
25+ self .head = node
26+ return node
27+
28+ def remove (self , node ):
29+ if self .head == self .tail and node == self .head :
30+ self .head = None
31+ self .tail = None
32+ elif node == self .head :
33+ self .head = self .head .next
34+ if self .head is not None :
35+ self .head .previous = None
36+ elif node == self .tail :
37+ self .tail = self .tail .previous
38+ if self .tail is not None :
39+ self .tail .next = None
40+ else :
41+ node .previous .next = node .next
42+ node .next .previous = node .previous
43+
44+ node .next = None
45+ node .previous = None
46+
47+ def pop_tail (self ):
48+ if self .tail is None :
49+ return None
50+ tail_node = self .tail
51+ self .remove (tail_node )
52+ return tail_node
53+
54+ def move_to_head (self , node ):
55+ if self .head == node :
56+ return
57+ self .remove (node )
58+ self .push_head (node )
59+
60+
1061class LruCache :
1162 # Initializes the cache with a maximum capacity
1263 def __init__ (self , limit ):
1364 if limit <= 0 :
1465 raise ValueError ("Cache limit must be greater than zero." )
1566 self .limit = limit
1667 self .cache = {} # Maps key -> Node for O(1) lookups
17- self .head = None # Most recently used
18- self .tail = None # Least recently used
68+ self .order_list = DoublyLinkedList () # Manages LRU order
1969
2070 # Looks up a value and marks it as recently used
2171 def get (self , key ):
2272 if key not in self .cache :
2373 return None
2474
2575 node = self .cache [key ]
26- self ._move_to_head (node )
76+ self .order_list . move_to_head (node )
2777 return node .value
2878
2979 # Add or update an item, evicting the oldest if full
3080 def set (self , key , value ):
3181 if key in self .cache :
3282 node = self .cache [key ]
3383 node .value = value
34- self ._move_to_head (node )
84+ self .order_list . move_to_head (node )
3585 return
3686
3787 if len (self .cache ) >= self .limit :
38- if self .tail is not None :
39- del self .cache [self .tail .key ]
40-
41- if self .head == self .tail :
42- self .head = None
43- self .tail = None
44- else :
45- self .tail = self .tail .previous
46- self .tail .next = None
88+ evicted_node = self .order_list .pop_tail ()
89+ if evicted_node is not None :
90+ del self .cache [evicted_node .key ]
4791
4892 new_node = Node (key , value )
4993 self .cache [key ] = new_node
50-
51- if self .head is None :
52- self .head = new_node
53- self .tail = new_node
54- else :
55- new_node .next = self .head
56- self .head .previous = new_node
57- self .head = new_node
58-
59- # Internal helper to reposition a used node to the front
60- def _move_to_head (self , node ):
61- if self .head == node :
62- return
63-
64- if node == self .tail :
65- self .tail = node .previous
66- self .tail .next = None
67- else :
68- node .previous .next = node .next
69- node .next .previous = node .previous
70-
71- node .next = self .head
72- self .head .previous = node
73- node .previous = None
74- self .head = node
94+ self .order_list .push_head (new_node )
0 commit comments