Skip to content

Commit 1fe37cb

Browse files
snowanazl397985856
authored andcommitted
1 parent e8be805 commit 1fe37cb

10 files changed

+242
-1
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
.DS_Store
1+
.DS_Store
2+
.idea/*

assets/problems/460.lfu-cache-1.jpg

122 KB
Loading

assets/problems/460.lfu-cache-2.jpg

133 KB
Loading

assets/problems/460.lfu-cache-3.jpg

183 KB
Loading

assets/problems/460.lfu-cache-4.jpg

175 KB
Loading

assets/problems/460.lfu-cache-5.jpg

192 KB
Loading

assets/problems/460.lfu-cache-6.jpg

224 KB
Loading

assets/problems/460.lfu-cache-7.jpg

189 KB
Loading

assets/problems/460.lfu-cache-8.jpg

267 KB
Loading

problems/460.lfu-cache.md

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
## 题目地址
2+
https://leetcode.com/problems/lfu-cache/
3+
4+
## 题目描述
5+
6+
```
7+
Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.
8+
9+
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
10+
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.
11+
12+
Follow up:
13+
Could you do both operations in O(1) time complexity?
14+
15+
Example:
16+
17+
LFUCache cache = new LFUCache( 2 /* capacity */ );
18+
19+
cache.put(1, 1);
20+
cache.put(2, 2);
21+
cache.get(1); // returns 1
22+
cache.put(3, 3); // evicts key 2
23+
cache.get(2); // returns -1 (not found)
24+
cache.get(3); // returns 3.
25+
cache.put(4, 4); // evicts key 1.
26+
cache.get(1); // returns -1 (not found)
27+
cache.get(3); // returns 3
28+
cache.get(4); // returns 4
29+
```
30+
31+
## 思路
32+
[LFU(Least frequently used)](https://www.wikiwand.com/en/Least_frequently_used) 但内存容量满的情况下,有新的数据进来,需要更多空间的时候,就需要删除被访问频率最少的元素。
33+
34+
举个例子,比如说cache容量是 3,按顺序依次放入 `1,2,1,2,1,3`, cache已存满 3 个元素 (1,2,3), 这时如果想放入一个新的元素 4 的时候,就需要腾出一个元素空间。
35+
用 LFU,这里就淘汰 3, 因为 3 的次数只出现依次, 1 和 2 出现的次数都比 3 多。
36+
37+
38+
题中 `get``put` 都是 `O(1)`的时间复杂度,那么删除和增加都是`O(1)`,可以想到用双链表,和`HashMap`,用一个`HashMap, nodeMap,` 保存当前`key`,和 `node{key, value, frequent} `的映射。
39+
这样`get(key)`的操作就是`O(1)`. 如果要删除一个元素,那么就需要另一个`HashMap,freqMap,`保存元素出现次数`(frequent)`和双链表`(DoublyLinkedlist)` 映射,
40+
这里双链表存的是frequent相同的元素。每次`get``put`的时候,`frequent+1`,然后把`node`插入到双链表的`head node, head.next=node`
41+
每次删除`freqent`最小的双链表的`tail node, tail.prev`
42+
43+
用给的例子举例说明:
44+
```
45+
1. put(1, 1),
46+
- 首先查找nodeMap中有没有key=1对应的value,
47+
没有就新建node(key, value, freq) -> node1(1, 1, 1), 插入 nodeMap,{[1, node1]}
48+
- 查找freqMap中有没有freq=1 对应的value,
49+
没有就新建doublylinkedlist(head, tail), 把node1 插入doublylinkedlist head->next = node1.
50+
如下图,
51+
```
52+
![460.lfu-cache-1](../assets/problems/460.lfu-cache-1.jpg)
53+
```
54+
2. put(2, 2),
55+
- 首先查找nodeMap中有没有key=2对应的value,
56+
没有就新建node(key, value, freq) -> node2(2, 2, 1), 插入 nodeMap,{[1, node1], [2, node2]}
57+
- 查找freqMap中有没有freq=1 对应的value,
58+
没有就新建doublylinkedlist(head, tail), 把node2 插入doublylinkedlist head->next = node2.
59+
如下图,
60+
```
61+
![460.lfu-cache-2](../assets/problems/460.lfu-cache-2.jpg)
62+
```
63+
3. get(1),
64+
- 首先查找nodeMap中有没有key=1对应的value,nodeMap:{[1, node1], [2, node2]},
65+
找到node1,把node1 freq+1 -> node1(1,1,2)
66+
- 更新freqMap,删除freq=1,node1
67+
- 更新freqMap,插入freq=2,node1
68+
如下图,
69+
```
70+
![460.lfu-cache-3](../assets/problems/460.lfu-cache-3.jpg)
71+
```
72+
4. put(3, 3),
73+
- 判断cache的capacity,已满,需要淘汰使用次数最少的元素,找到最小的freq=1,删除双链表tail node.prev
74+
如果tailnode.prev != null, 删除。然后从nodeMap中删除对应的key。
75+
- 首先查找nodeMap中有没有key=3对应的value,
76+
没有就新建node(key, value, freq) -> node3(3, 3, 1), 插入 nodeMap,{[1, node1], [3, node3]}
77+
- 查找freqMap中有没有freq=1 对应的value,
78+
没有就新建doublylinkedlist(head, tail), 把node3 插入doublylinkedlist head->next = node3.
79+
如下图,
80+
```
81+
![460.lfu-cache-4](../assets/problems/460.lfu-cache-4.jpg)
82+
```
83+
5. get(2)
84+
- 查找nodeMap,如果没有对应的key的value,返回 -1。
85+
86+
6. get(3)
87+
- 首先查找nodeMap中有没有key=3对应的value,nodeMap:{[1, node1], [3, node3]},
88+
找到node3,把node3 freq+1 -> node3(3,3,2)
89+
- 更新freqMap,删除freq=1,node3
90+
- 更新freqMap,插入freq=2,node3
91+
如下图,
92+
```
93+
![460.lfu-cache-5](../assets/problems/460.lfu-cache-5.jpg)
94+
```
95+
7. put(4, 4),
96+
- 判断cache的capacity,已满,需要淘汰使用次数最少的元素,找到最小的freq=1,删除双链表tail node.prev
97+
如果tailnode.prev != null, 删除。然后从nodeMap中删除对应的key。
98+
- 首先查找nodeMap中有没有key=4对应的value,
99+
没有就新建node(key, value, freq) -> node4(4, 4, 1), 插入 nodeMap,{[4, node4], [3, node3]}
100+
- 查找freqMap中有没有freq=1 对应的value,
101+
没有就新建doublylinkedlist(head, tail), 把 node4 插入doublylinkedlist head->next = node4.
102+
如下图,
103+
```
104+
![460.lfu-cache-6](../assets/problems/460.lfu-cache-6.jpg)
105+
```
106+
8. get(1)
107+
- 查找nodeMap,如果没有对应的key的value,返回 -1。
108+
109+
9. get(3)
110+
- 首先查找nodeMap中有没有key=3对应的value,nodeMap:{[4, node4], [3, node3]},
111+
找到node3,把node3 freq+1 -> node3(3,3,3)
112+
- 更新freqMap,删除freq=2,node3
113+
- 更新freqMap,插入freq=3,node3
114+
如下图,
115+
```
116+
![460.lfu-cache-7](../assets/problems/460.lfu-cache-7.jpg)
117+
```
118+
10. get(4)
119+
- 首先查找nodeMap中有没有key=4对应的value,nodeMap:{[4, node4], [3, node3]},
120+
找到node4,把node4 freq+1 -> node4(4,4,2)
121+
- 更新freqMap,删除freq=1,node4
122+
- 更新freqMap,插入freq=2,node4
123+
如下图,
124+
```
125+
![460.lfu-cache-8](../assets/problems/460.lfu-cache-8.jpg)
126+
127+
## 关键点分析
128+
用两个`Map`分别保存 `nodeMap {key, node}``freqMap{frequent, DoublyLinkedList}`
129+
实现`get``put`操作都是`O(1)`的时间复杂度。
130+
131+
可以用Java自带的一些数据结构,比如HashLinkedHashSet,这样就不需要自己自建Node,DoublelyLinkedList。
132+
可以很大程度的缩减代码量。
133+
134+
## 代码(Java code)
135+
```java
136+
public class LC460LFUCache {
137+
class Node {
138+
int key, val, freq;
139+
Node prev, next;
140+
141+
Node(int key, int val) {
142+
this.key = key;
143+
this.val = val;
144+
freq = 1;
145+
}
146+
}
147+
148+
class DoubleLinkedList {
149+
private Node head;
150+
private Node tail;
151+
private int size;
152+
153+
DoubleLinkedList() {
154+
head = new Node(0, 0);
155+
tail = new Node(0, 0);
156+
head.next = tail;
157+
tail.prev = head;
158+
}
159+
160+
void add(Node node) {
161+
head.next.prev = node;
162+
node.next = head.next;
163+
node.prev = head;
164+
head.next = node;
165+
size++;
166+
}
167+
168+
void remove(Node node) {
169+
node.prev.next = node.next;
170+
node.next.prev = node.prev;
171+
size--;
172+
}
173+
174+
// always remove last node if last node exists
175+
Node removeLast() {
176+
if (size > 0) {
177+
Node node = tail.prev;
178+
remove(node);
179+
return node;
180+
} else return null;
181+
}
182+
}
183+
184+
// cache capacity
185+
private int capacity;
186+
// min frequent
187+
private int minFreq;
188+
Map<Integer, Node> nodeMap;
189+
Map<Integer, DoubleLinkedList> freqMap;
190+
public LC460LFUCache(int capacity) {
191+
this.minFreq = 0;
192+
this.capacity = capacity;
193+
nodeMap = new HashMap<>();
194+
freqMap = new HashMap<>();
195+
}
196+
197+
public int get(int key) {
198+
Node node = nodeMap.get(key);
199+
if (node == null) return -1;
200+
update(node);
201+
return node.val;
202+
}
203+
204+
public void put(int key, int value) {
205+
if (capacity == 0) return;
206+
Node node;
207+
if (nodeMap.containsKey(key)) {
208+
node = nodeMap.get(key);
209+
node.val = value;
210+
update(node);
211+
} else {
212+
node = new Node(key, value);
213+
nodeMap.put(key, node);
214+
if (nodeMap.size() == capacity) {
215+
DoubleLinkedList lastList = freqMap.get(minFreq);
216+
nodeMap.remove(lastList.removeLast().key);
217+
}
218+
minFreq = 1;
219+
DoubleLinkedList newList = freqMap.getOrDefault(node.freq, new DoubleLinkedList());
220+
newList.add(node);
221+
freqMap.put(node.freq, newList);
222+
}
223+
}
224+
225+
private void update(Node node) {
226+
DoubleLinkedList oldList = freqMap.get(node.freq);
227+
oldList.remove(node);
228+
if (node.freq == minFreq && oldList.size == 0) minFreq++;
229+
node.freq++;
230+
DoubleLinkedList newList = freqMap.getOrDefault(node.freq, new DoubleLinkedList());
231+
newList.add(node);
232+
freqMap.put(node.freq, newList);
233+
}
234+
}
235+
```
236+
237+
## 参考(References)
238+
1. [LFU(Least frequently used) Cache](https://www.wikiwand.com/en/Least_frequently_used)
239+
2. [Leetcode discussion mylzsd](https://leetcode.com/problems/lfu-cache/discuss/94547/Java-O(1)-Solution-Using-Two-HashMap-and-One-DoubleLinkedList)
240+
3. [Leetcode discussion aaaeeeo](https://leetcode.com/problems/lfu-cache/discuss/94547/Java-O(1)-Solution-Using-Two-HashMap-and-One-DoubleLinkedList)

0 commit comments

Comments
 (0)