-
Notifications
You must be signed in to change notification settings - Fork 17
/
lfu-cache.py
143 lines (105 loc) · 4.15 KB
/
lfu-cache.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from typing import Dict, Optional
class DoubleLinkedListNode:
def __init__(self, value: int) -> None:
self.value = value
self.key: Optional[int] = None
self.frequency: Optional[DoubleLinkedListNode] = None
self.tail: Optional[DoubleLinkedListNode] = None
self.next: Optional[DoubleLinkedListNode] = None
self.prev: Optional[DoubleLinkedListNode] = None
def remove_node(node: DoubleLinkedListNode) -> None:
prev_node = node.prev
next_node = node.next
if prev_node:
prev_node.next = next_node
if next_node:
next_node.prev = prev_node
def insert_after(node: DoubleLinkedListNode, after: DoubleLinkedListNode) -> None:
prev_node = after
next_node = after.next
if prev_node:
prev_node.next = node
node.prev = prev_node
if next_node:
next_node.prev = node
node.next = next_node
class LFUCache:
def __init__(self, capacity: int) -> None:
self._count: int = 0
self._capacity: int = capacity
self._key_map: Dict[int, DoubleLinkedListNode] = {}
self._head: DoubleLinkedListNode = DoubleLinkedListNode(0)
self._frequency_head: DoubleLinkedListNode = DoubleLinkedListNode(0)
self._frequency_head.tail = self._head
self._head.frequency = self._frequency_head
def _get_next_freq_or_create(
self, frequency: DoubleLinkedListNode,
) -> DoubleLinkedListNode:
frequency_next = frequency.next
if frequency.next and frequency.value + 1 == frequency.next.value:
pass
else:
frequency_next = DoubleLinkedListNode(frequency.value + 1)
insert_after(frequency_next, frequency)
if not frequency_next:
raise ValueError("next frequency is not set")
return frequency_next
def _insert_into_frequency(
self, frequency: DoubleLinkedListNode, node: DoubleLinkedListNode
) -> None:
move_node_after = frequency.tail or frequency.prev.tail
insert_after(node, move_node_after)
node.frequency = frequency
frequency.tail = node
def _remove_node_with_freq(self, node: DoubleLinkedListNode) -> None:
frequency = node.frequency
if not frequency:
raise ValueError("node should have frequency")
# Remove node from the list and update the current frequency
remove_node(node)
if node == frequency.tail:
frequency.tail = frequency.tail.prev
frequency_tail = frequency.tail
if not frequency_tail:
raise ValueError("Frequency tail should not be empty at this point")
if frequency_tail.frequency != frequency:
remove_node(frequency)
def touch(self, key: int) -> None:
node = self._key_map.get(key)
if not node:
return
frequency = node.frequency
node_prev = node.prev
node_next = node.next
if not frequency:
raise ValueError("Node should always have a frequency")
# Remove node from the list and update the current frequency
self._remove_node_with_freq(node)
# Get or create the next frequency
frequency_next = self._get_next_freq_or_create(frequency)
# Insert node into the end of the current frequency
self._insert_into_frequency(frequency_next, node)
def get(self, key: int) -> int:
self.touch(key)
node = self._key_map.get(key)
if not node:
return -1
return node.value
def put(self, key: int, value: int) -> None:
if self._capacity == 0:
return
node = self._key_map.get(key)
if node:
self.touch(key)
node.value = value
else:
if self._count + 1 > self._capacity:
self._key_map.pop(self._head.next.key)
self._remove_node_with_freq(self._head.next)
self._count -= 1
node = DoubleLinkedListNode(value)
node.key = key
self._key_map[key] = node
self._insert_into_frequency(self._frequency_head, node)
self.touch(key)
self._count += 1