Skip to content

Commit 4240dff

Browse files
authored
feat: add solutions to lc problem: No.0460 (doocs#1675)
No.0460.LFU Cache
1 parent 8d2ba7f commit 4240dff

File tree

4 files changed

+655
-9
lines changed

4 files changed

+655
-9
lines changed

solution/0400-0499/0460.LFU Cache/README.md

Lines changed: 232 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,28 @@ lfu.get(4); // 返回 4
6969

7070
<!-- 这里可写通用的实现逻辑 -->
7171

72-
[LRU 缓存](/solution/0100-0199/0146.Lru%20Cache/README.md) 类似的思路,用 `map<key, node>``map<freq, list<node>>` 存储不同使用频率的节点
72+
**方法一:双哈希表 + 双向链表**
7373

74-
对于 `get` 操作,判断 key 是否存在哈希表中
74+
我们定义两个哈希表,其中
7575

76-
- 若不存在,返回 -1
77-
- 若存在,增加节点的使用频率,返回节点值
76+
- 哈希表 $map$:用于存储缓存的键值对,哈希表的键 $key$ 对应到缓存节点 $node$,方便 $O(1)$ 时间内获取缓存节点。
77+
- 哈希表 $freqMap$:用于存储使用频率相同的缓存节点的双向链表,哈希表的键 $freq$ 对应到双向链表 $list$,方便 $O(1)$ 时间内获取使用频率相同的缓存节点的双向链表。
7878

79-
对于 `put` 操作,同样判断 key 是否存在哈希表中:
79+
另外,我们还需要维护一个变量 $minFreq$,用于记录当前最小的使用频率,方便 $O(1)$ 时间内获取最小使用频率的缓存节点。
8080

81-
- 若不存在,首先判断缓存容量是否足够,不够的话需要先删除使用次数最少的节点。然后再创建新节点,插入使用频率为 1 的双链表
82-
- 若存在,修改原节点的值,增加节点的使用频率
81+
对于 $get(key)$ 操作:
82+
83+
我们首先判断 $capacity$ 是否为 $0$ 或者 $map$ 中是否存在键 $key$,如果不存在则返回 $-1$;否则从 $map$ 中获取缓存节点 $node$,并将 $node$ 的使用频率加 $1$,最后返回 $node$ 的值。
84+
85+
对于 $put(key, value)$ 操作:
86+
87+
我们首先判断 $capacity$ 是否为 $0$,如果为 $0$ 则直接返回;
88+
89+
否则判断 $map$ 中是否存在键 $key$,如果存在则从 $map$ 中获取缓存节点 $node$,更新 $node$ 的值为 $value$,并将 $node$ 的使用频率加 $1$,最后返回 $node$ 的值;
90+
91+
如果不存在则判断 $map$ 的长度是否等于 $capacity$,如果等于 $capacity$ 则从 $freqMap$ 中获取使用频率最小的双向链表 $list$,从 $list$ 中删除最后一个节点,并且移除该节点对应的键值对。然后创建新的缓存节点 $node$,将 $node$ 的使用频率设置为 $1$,将 $node$ 添加到 $map$ 和 $freqMap$ 中,最后将 $minFreq$ 设置为 $1$。
92+
93+
时间复杂度方面,操作 $get$ 和 $put$ 的时间复杂度都是 $O(1)$。空间复杂度 $O(n)$,其中 $n$ 为缓存的容量。
8394

8495
<!-- tabs:start -->
8596

@@ -88,7 +99,94 @@ lfu.get(4); // 返回 4
8899
<!-- 这里可写当前语言的特殊实现逻辑 -->
89100

90101
```python
91-
102+
class Node:
103+
def __init__(self, key: int, value: int) -> None:
104+
self.key = key
105+
self.value = value
106+
self.freq = 1
107+
self.prev = None
108+
self.next = None
109+
110+
111+
class DoublyLinkedList:
112+
def __init__(self) -> None:
113+
self.head = Node(-1, -1)
114+
self.tail = Node(-1, -1)
115+
self.head.next = self.tail
116+
self.tail.prev = self.head
117+
118+
def add_first(self, node: Node) -> None:
119+
node.prev = self.head
120+
node.next = self.head.next
121+
self.head.next.prev = node
122+
self.head.next = node
123+
124+
def remove(self, node: Node) -> Node:
125+
node.next.prev = node.prev
126+
node.prev.next = node.next
127+
node.next, node.prev = None, None
128+
return node
129+
130+
def remove_last(self) -> Node:
131+
return self.remove(self.tail.prev)
132+
133+
def is_empty(self) -> bool:
134+
return self.head.next == self.tail
135+
136+
137+
class LFUCache:
138+
def __init__(self, capacity: int):
139+
self.capacity = capacity
140+
self.min_freq = 0
141+
self.map = defaultdict(Node)
142+
self.freq_map = defaultdict(DoublyLinkedList)
143+
144+
def get(self, key: int) -> int:
145+
if self.capacity == 0 or key not in self.map:
146+
return -1
147+
node = self.map[key]
148+
self.incr_freq(node)
149+
return node.value
150+
151+
def put(self, key: int, value: int) -> None:
152+
if self.capacity == 0:
153+
return
154+
if key in self.map:
155+
node = self.map[key]
156+
node.value = value
157+
self.incr_freq(node)
158+
return
159+
if len(self.map) == self.capacity:
160+
ls = self.freq_map[self.min_freq]
161+
node = ls.remove_last()
162+
self.map.pop(node.key)
163+
node = Node(key, value)
164+
self.add_node(node)
165+
self.map[key] = node
166+
self.min_freq = 1
167+
168+
def incr_freq(self, node: Node) -> None:
169+
freq = node.freq
170+
ls = self.freq_map[freq]
171+
ls.remove(node)
172+
if ls.is_empty():
173+
self.freq_map.pop(freq)
174+
if freq == self.min_freq:
175+
self.min_freq += 1
176+
node.freq += 1
177+
self.add_node(node)
178+
179+
def add_node(self, node: Node) -> None:
180+
freq = node.freq
181+
ls = self.freq_map[freq]
182+
ls.add_first(node)
183+
self.freq_map[freq] = ls
184+
185+
186+
# Your LFUCache object will be instantiated and called as such:
187+
# obj = LFUCache(capacity)
188+
# param_1 = obj.get(key)
189+
# obj.put(key,value)
92190
```
93191

94192
### **Java**
@@ -214,6 +312,132 @@ class LFUCache {
214312
}
215313
```
216314

315+
### **C++**
316+
317+
```cpp
318+
class Node {
319+
public:
320+
int key;
321+
int value;
322+
int freq;
323+
Node* prev;
324+
Node* next;
325+
Node(int key, int value) {
326+
this->key = key;
327+
this->value = value;
328+
this->freq = 1;
329+
this->prev = nullptr;
330+
this->next = nullptr;
331+
}
332+
};
333+
334+
class DoublyLinkedList {
335+
public:
336+
Node* head;
337+
Node* tail;
338+
DoublyLinkedList() {
339+
this->head = new Node(-1, -1);
340+
this->tail = new Node(-1, -1);
341+
this->head->next = this->tail;
342+
this->tail->prev = this->head;
343+
}
344+
void addFirst(Node* node) {
345+
node->prev = this->head;
346+
node->next = this->head->next;
347+
this->head->next->prev = node;
348+
this->head->next = node;
349+
}
350+
Node* remove(Node* node) {
351+
node->next->prev = node->prev;
352+
node->prev->next = node->next;
353+
node->next = nullptr;
354+
node->prev = nullptr;
355+
return node;
356+
}
357+
Node* removeLast() {
358+
return remove(this->tail->prev);
359+
}
360+
bool isEmpty() {
361+
return this->head->next == this->tail;
362+
}
363+
};
364+
365+
class LFUCache {
366+
public:
367+
LFUCache(int capacity) {
368+
this->capacity = capacity;
369+
this->minFreq = 0;
370+
}
371+
372+
int get(int key) {
373+
if (capacity == 0 || map.find(key) == map.end()) {
374+
return -1;
375+
}
376+
Node* node = map[key];
377+
incrFreq(node);
378+
return node->value;
379+
}
380+
381+
void put(int key, int value) {
382+
if (capacity == 0) {
383+
return;
384+
}
385+
if (map.find(key) != map.end()) {
386+
Node* node = map[key];
387+
node->value = value;
388+
incrFreq(node);
389+
return;
390+
}
391+
if (map.size() == capacity) {
392+
DoublyLinkedList* list = freqMap[minFreq];
393+
Node* node = list->removeLast();
394+
map.erase(node->key);
395+
}
396+
Node* node = new Node(key, value);
397+
addNode(node);
398+
map[key] = node;
399+
minFreq = 1;
400+
}
401+
402+
private:
403+
int capacity;
404+
int minFreq;
405+
unordered_map<int, Node*> map;
406+
unordered_map<int, DoublyLinkedList*> freqMap;
407+
408+
void incrFreq(Node* node) {
409+
int freq = node->freq;
410+
DoublyLinkedList* list = freqMap[freq];
411+
list->remove(node);
412+
if (list->isEmpty()) {
413+
freqMap.erase(freq);
414+
if (freq == minFreq) {
415+
minFreq++;
416+
}
417+
}
418+
node->freq++;
419+
addNode(node);
420+
}
421+
422+
void addNode(Node* node) {
423+
int freq = node->freq;
424+
if (freqMap.find(freq) == freqMap.end()) {
425+
freqMap[freq] = new DoublyLinkedList();
426+
}
427+
DoublyLinkedList* list = freqMap[freq];
428+
list->addFirst(node);
429+
freqMap[freq] = list;
430+
}
431+
};
432+
433+
/**
434+
* Your LFUCache object will be instantiated and called as such:
435+
* LFUCache* obj = new LFUCache(capacity);
436+
* int param_1 = obj->get(key);
437+
* obj->put(key,value);
438+
*/
439+
```
440+
217441
### **Go**
218442
219443
```go

0 commit comments

Comments
 (0)