@@ -69,17 +69,28 @@ lfu.get(4); // 返回 4
69
69
70
70
<!-- 这里可写通用的实现逻辑 -->
71
71
72
- 和 [ LRU 缓存 ] ( /solution/0100-0199/0146.Lru%20Cache/README.md ) 类似的思路,用 ` map<key, node> ` 和 ` map<freq, list<node>> ` 存储不同使用频率的节点
72
+ ** 方法一:双哈希表 + 双向链表 **
73
73
74
- 对于 ` get ` 操作,判断 key 是否存在哈希表中 :
74
+ 我们定义两个哈希表,其中 :
75
75
76
- - 若不存在,返回 -1
77
- - 若存在,增加节点的使用频率,返回节点值
76
+ - 哈希表 $map$:用于存储缓存的键值对,哈希表的键 $key$ 对应到缓存节点 $node$,方便 $O(1)$ 时间内获取缓存节点。
77
+ - 哈希表 $freqMap$:用于存储使用频率相同的缓存节点的双向链表,哈希表的键 $freq$ 对应到双向链表 $list$,方便 $O(1)$ 时间内获取使用频率相同的缓存节点的双向链表。
78
78
79
- 对于 ` put ` 操作,同样判断 key 是否存在哈希表中:
79
+ 另外,我们还需要维护一个变量 $minFreq$,用于记录当前最小的使用频率,方便 $O(1)$ 时间内获取最小使用频率的缓存节点。
80
80
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$ 为缓存的容量。
83
94
84
95
<!-- tabs:start -->
85
96
@@ -88,7 +99,94 @@ lfu.get(4); // 返回 4
88
99
<!-- 这里可写当前语言的特殊实现逻辑 -->
89
100
90
101
``` 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)
92
190
```
93
191
94
192
### ** Java**
@@ -214,6 +312,132 @@ class LFUCache {
214
312
}
215
313
```
216
314
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
+
217
441
### **Go**
218
442
219
443
```go
0 commit comments