1+ """
2+ An implementation of a Least Recently Used (LRU) Cache.
3+
4+ The LRU caching scheme is used to remove items from the cache to make
5+ room for new ones. It discards the least recently used items first.
6+ This implementation uses a hash map (dictionary) for O(1) lookups and a
7+ doubly linked list to maintain the order of usage, allowing for O(1)
8+ updates (insertion and deletion).
9+
10+ For more information on the algorithm, see:
11+ https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)
12+ """
13+
14+ class Node :
15+ """A node in the doubly linked list."""
16+ def __init__ (self , key : int , val : int ) -> None :
17+ self .key , self .val = key , val
18+ self .prev = self .next = None
19+
20+ class LRUCache :
21+ """
22+ A Least Recently Used (LRU) Cache data structure.
23+ >>> cache = LRUCache(2)
24+ >>> cache.put(1, 1)
25+ >>> cache.put(2, 2)
26+ >>> cache.get(1)
27+ 1
28+ >>> cache.put(3, 3) # evicts key 2
29+ >>> cache.get(2)
30+ -1
31+ >>> cache.put(4, 4) # evicts key 1
32+ >>> cache.get(1)
33+ -1
34+ >>> cache.get(3)
35+ 3
36+ >>> cache.get(4)
37+ 4
38+ """
39+ def __init__ (self , capacity : int ) -> None :
40+ if capacity <= 0 :
41+ raise ValueError ("Capacity must be a positive integer." )
42+ self .capacity = capacity
43+ self .cache = {} # Maps key to node
44+
45+ self .head = Node (- 1 , - 1 )
46+ self .tail = Node (- 1 , - 1 )
47+ self .head .next = self .tail
48+ self .tail .prev = self .head
49+
50+ def _remove (self , node : Node ) -> None :
51+ prev , nxt = node .prev , node .next
52+ prev .next = nxt
53+ nxt .prev = prev
54+
55+ def _add (self , node : Node ) -> None :
56+ node .prev = self .head
57+ node .next = self .head .next
58+ self .head .next .prev = node
59+ self .head .next = node
60+
61+ def get (self , key : int ) -> int :
62+ if key in self .cache :
63+ node = self .cache [key ]
64+ self ._remove (node )
65+ self ._add (node )
66+ return node .val
67+ return - 1
68+
69+ def put (self , key : int , value : int ) -> None :
70+ if key in self .cache :
71+ node = self .cache [key ]
72+ node .val = value
73+ self ._remove (node )
74+ self ._add (node )
75+ else :
76+ new_node = Node (key , value )
77+ self .cache [key ] = new_node
78+ self ._add (new_node )
79+
80+ if len (self .cache ) > self .capacity :
81+ lru = self .tail .prev
82+ self ._remove (lru )
83+ del self .cache [lru .key ]
84+
85+ if __name__ == "__main__" :
86+ import doctest
87+ doctest .testmod ()
0 commit comments