Skip to content

Commit f3bd926

Browse files
committed
Separate the doubly linked list from the LRU cache
1 parent 40d89e8 commit f3bd926

1 file changed

Lines changed: 45 additions & 65 deletions

File tree

Lines changed: 45 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,64 @@
1-
class Node:
2-
__slots__ = ("key", "value", "prev", "next")
3-
1+
class DLLNode:
42
def __init__(self, key, value):
53
self.key = key
64
self.value = value
75
self.prev = None
86
self.next = None
97

108

11-
class LruCache:
12-
def __init__(self, limit: int):
13-
if limit < 1:
14-
raise ValueError(f"limit must be at least 1, got {limit}")
9+
class DoublyLinkedList:
10+
def __init__(self):
11+
self.head = DLLNode(None, None)
12+
self.tail = DLLNode(None, None)
13+
self.head.next = self.tail
14+
self.tail.prev = self.head
1515

16-
self._limit = limit
17-
self.map: dict = {}
18-
self.head = None
19-
self.tail = None
16+
def add_to_front(self, node):
17+
node.next = self.head.next
18+
node.prev = self.head
19+
self.head.next.prev = node
20+
self.head.next = node
2021

21-
def _remove_node(self, node: Node):
22-
"""Remove a node from the linked list in O(1) time."""
23-
if node.prev:
24-
node.prev.next = node.next
25-
else:
26-
self.head = node.next
22+
def remove(self, node):
23+
node.prev.next = node.next
24+
node.next.prev = node.prev
2725

28-
if node.next:
29-
node.next.prev = node.prev
30-
else:
31-
self.tail = node.prev
32-
33-
node.prev = None
34-
node.next = None
26+
def move_to_front(self, node):
27+
self.remove(node)
28+
self.add_to_front(node)
3529

36-
def _add_node_to_head(self, node: Node):
37-
"""Add a node to the head of the linked list in O(1) time."""
38-
node.next = self.head
39-
node.prev = None
30+
def remove_last(self):
31+
if self.tail.prev is self.head:
32+
return None
33+
last = self.tail.prev
34+
self.remove(last)
35+
return last
4036

41-
if self.head:
42-
self.head.prev = node
43-
else:
44-
self.tail = node
45-
46-
self.head = node
37+
38+
class LruCache:
39+
def __init__(self, capacity: int):
40+
self.capacity = capacity
41+
self.cache = {}
42+
self.list = DoublyLinkedList()
4743

4844
def get(self, key):
49-
"""Return the value for key, Non if not present"""
50-
node = self.map.get(key)
51-
if node is None:
52-
return None
53-
54-
self._touch(node)
45+
if key not in self.cache:
46+
return -1
47+
node = self.cache[key]
48+
self.list.move_to_front(node)
5549
return node.value
5650

57-
def set(self, key, value) -> None:
58-
"""Combine value with key, evicting the LRU entry if necessary."""
59-
node = self.map.get(key)
60-
61-
if node:
51+
def put(self, key, value):
52+
if key in self.cache:
53+
node = self.cache[key]
6254
node.value = value
63-
self._touch(node)
64-
else:
65-
if len(self.map) >= self._limit:
66-
self._evict()
67-
68-
new_node = Node(key, value)
69-
self._add_node_to_head(new_node)
70-
self.map[key] = new_node
55+
self.list.move_to_front(node)
56+
return
7157

72-
def _touch(self, node) -> None:
73-
"""Move an existing node to the head (most-recently-used position)."""
74-
self._remove_node(node)
75-
self._add_node_to_head(node)
58+
new_node = DLLNode(key, value)
59+
self.cache[key] = new_node
60+
self.list.add_to_front(new_node)
7661

77-
def _evict(self) -> None:
78-
"""Remove the least-recently-used entry (tail of the list)."""
79-
if self.tail is None:
80-
return
81-
82-
lru = self.tail
83-
self._remove_node(lru)
84-
del self.map[lru.key]
62+
if len(self.cache) > self.capacity:
63+
last = self.list.remove_last()
64+
del self.cache[last.key]

0 commit comments

Comments
 (0)