Skip to content

Commit 104958c

Browse files
committed
+ problem 460
1 parent fd32275 commit 104958c

File tree

5 files changed

+348
-0
lines changed

5 files changed

+348
-0
lines changed

Problemset/0460-LFU Cache/README.md

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# 460. LFU Cache
2+
Design and implement a data structure for a [Least Frequently Used (LFU)](https://en.wikipedia.org/wiki/Least_frequently_used) cache.
3+
4+
Implement the `LFUCache` class:
5+
* `LFUCache(int capacity)` Initializes the object with the `capacity` of the data structure.
6+
* `int get(int key)` Gets the value of the `key` if the `key` exists in the cache. Otherwise, returns `-1`.
7+
* `void put(int key, int value)` Update the value of the `key` if present, or inserts the `key` if not already present. When the cache reaches its `capacity`, it should invalidate and remove the **least frequently used** key before inserting a new item. For this problem, when there is a **tie** (i.e., two or more keys with the same frequency), the **least recently used** `key` would be invalidated.
8+
9+
To determine the least frequently used key, a **use counter** is maintained for each key in the cache. The key with the smallest **use counter** is the least frequently used key.
10+
11+
When a key is first inserted into the cache, its **use counter** is set to `1` (due to the `put` operation). The **use counter** for a key in the cache is incremented either a `get` or `put` operation is called on it.
12+
13+
The functions `get` and `put` must each run in `O(1)` average time complexity.
14+
15+
#### Example 1:
16+
<pre>
17+
<strong>Input:</strong>
18+
["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"]
19+
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]
20+
<strong>Output:</strong>
21+
[null, null, null, 1, null, -1, 3, null, -1, 3, 4]
22+
<strong>Explanation:</strong>
23+
// cnt(x) = the use counter for key x
24+
// cache=[] will show the last used order for tiebreakers (leftmost element is most recent)
25+
LFUCache lfu = new LFUCache(2);
26+
lfu.put(1, 1); // cache=[1,_], cnt(1)=1
27+
lfu.put(2, 2); // cache=[2,1], cnt(2)=1, cnt(1)=1
28+
lfu.get(1); // return 1
29+
// cache=[1,2], cnt(2)=1, cnt(1)=2
30+
lfu.put(3, 3); // 2 is the LFU key because cnt(2)=1 is the smallest, invalidate 2.
31+
// cache=[3,1], cnt(3)=1, cnt(1)=2
32+
lfu.get(2); // return -1 (not found)
33+
lfu.get(3); // return 3
34+
// cache=[3,1], cnt(3)=2, cnt(1)=2
35+
lfu.put(4, 4); // Both 1 and 3 have the same cnt, but 1 is LRU, invalidate 1.
36+
// cache=[4,3], cnt(4)=1, cnt(3)=2
37+
lfu.get(1); // return -1 (not found)
38+
lfu.get(3); // return 3
39+
// cache=[3,4], cnt(4)=1, cnt(3)=3
40+
lfu.get(4); // return 4
41+
// cache=[4,3], cnt(4)=2, cnt(3)=3
42+
</pre>
43+
44+
#### Constraints:
45+
* <code>1 <= capacity <= 10<sup>4</sup></code>
46+
* <code>0 <= key <= 10<sup>5</sup></code>
47+
* <code>0 <= value <= 10<sup>9</sup></code>
48+
* At most <code>2 * 10<sup>5</sup></code> calls will be made to `get` and `put`.
49+
50+
## Solutions (Python)
51+
52+
### 1. Solution
53+
```Python
54+
class ListNode:
55+
56+
def __init__(self, key=-1, val=0, prev=None, next=None):
57+
self.key = key
58+
self.val = val
59+
self.prev = prev
60+
self.next = next
61+
self.count = 0
62+
63+
64+
class LFUCache:
65+
66+
def __init__(self, capacity: int):
67+
self.capacity = capacity
68+
self.cache = {}
69+
self.headtails = {}
70+
self.hair = ListNode()
71+
72+
def get(self, key: int) -> int:
73+
if key not in self.cache:
74+
return -1
75+
76+
node = self.cache[key]
77+
head, tail = self.headtails[node.count]
78+
if head.key == node.key and tail.key == node.key:
79+
self.headtails.pop(node.count)
80+
elif head.key == node.key:
81+
self.headtails[node.count][0] = node.next
82+
elif tail.key == node.key:
83+
self.headtails[node.count][1] = node.prev
84+
node.prev.next = node.next
85+
if node.next is not None:
86+
node.next.prev = node.prev
87+
88+
node.count += 1
89+
if tail.next is None or tail.next.count > node.count:
90+
if tail.key == node.key:
91+
tail = node.prev
92+
self.headtails[node.count] = [node, node]
93+
else:
94+
tail = self.headtails[node.count][1]
95+
self.headtails[node.count][1] = node
96+
node.prev = tail
97+
node.next = tail.next
98+
tail.next = node
99+
if node.next is not None:
100+
node.next.prev = node
101+
102+
return node.val
103+
104+
def put(self, key: int, value: int) -> None:
105+
if key not in self.cache:
106+
if len(self.cache) == self.capacity:
107+
head, tail = self.headtails[self.hair.next.count]
108+
self.cache.pop(head.key)
109+
if head.key == tail.key:
110+
self.headtails.pop(head.count)
111+
else:
112+
self.headtails[head.count][0] = head.next
113+
self.hair.next = head.next
114+
if head.next is not None:
115+
head.next.prev = self.hair
116+
117+
node = ListNode(key, value, self.hair, self.hair.next)
118+
self.cache[key] = node
119+
self.headtails[0] = [node, node]
120+
self.hair.next = node
121+
if node.next is not None:
122+
node.next.prev = node
123+
124+
self.cache[key].val = value
125+
self.get(key)
126+
127+
128+
# Your LFUCache object will be instantiated and called as such:
129+
# obj = LFUCache(capacity)
130+
# param_1 = obj.get(key)
131+
# obj.put(key,value)
132+
```
+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# 460. LFU 缓存
2+
请你为 [最不经常使用(LFU)](https://baike.baidu.com/item/%E7%BC%93%E5%AD%98%E7%AE%97%E6%B3%95)缓存算法设计并实现数据结构。
3+
4+
实现 `LFUCache` 类:
5+
* `LFUCache(int capacity)` - 用数据结构的容量 `capacity` 初始化对象
6+
* `int get(int key)` - 如果键 `key` 存在于缓存中,则获取键的值,否则返回 `-1`
7+
* `void put(int key, int value)` - 如果键 `key` 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 `capacity` 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 **最久未使用** 的键。
8+
9+
为了确定最不常使用的键,可以为缓存中的每个键维护一个 **使用计数器** 。使用计数最小的键是最久未使用的键。
10+
11+
当一个键首次插入到缓存中时,它的使用计数器被设置为 `1` (由于 put 操作)。对缓存中的键执行 `get``put` 操作,使用计数器的值将会递增。
12+
13+
函数 `get``put` 必须以 `O(1)` 的平均时间复杂度运行。
14+
15+
#### 示例:
16+
<pre>
17+
<strong>输入:</strong>
18+
["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"]
19+
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]
20+
<strong>输出:</strong>
21+
[null, null, null, 1, null, -1, 3, null, -1, 3, 4]
22+
<strong>解释:</strong>
23+
// cnt(x) = 键 x 的使用计数
24+
// cache=[] 将显示最后一次使用的顺序(最左边的元素是最近的)
25+
LFUCache lfu = new LFUCache(2);
26+
lfu.put(1, 1); // cache=[1,_], cnt(1)=1
27+
lfu.put(2, 2); // cache=[2,1], cnt(2)=1, cnt(1)=1
28+
lfu.get(1); // 返回 1
29+
// cache=[1,2], cnt(2)=1, cnt(1)=2
30+
lfu.put(3, 3); // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小
31+
// cache=[3,1], cnt(3)=1, cnt(1)=2
32+
lfu.get(2); // 返回 -1(未找到)
33+
lfu.get(3); // 返回 3
34+
// cache=[3,1], cnt(3)=2, cnt(1)=2
35+
lfu.put(4, 4); // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用
36+
// cache=[4,3], cnt(4)=1, cnt(3)=2
37+
lfu.get(1); // 返回 -1(未找到)
38+
lfu.get(3); // 返回 3
39+
// cache=[3,4], cnt(4)=1, cnt(3)=3
40+
lfu.get(4); // 返回 4
41+
// cache=[3,4], cnt(4)=2, cnt(3)=3
42+
</pre>
43+
44+
#### 提示:
45+
* <code>1 <= capacity <= 10<sup>4</sup></code>
46+
* <code>0 <= key <= 10<sup>5</sup></code>
47+
* <code>0 <= value <= 10<sup>9</sup></code>
48+
* 最多调用 <code>2 * 10<sup>5</sup></code> 次 `get``put` 方法
49+
50+
## 题解 (Python)
51+
52+
### 1. 题解
53+
```Python
54+
class ListNode:
55+
56+
def __init__(self, key=-1, val=0, prev=None, next=None):
57+
self.key = key
58+
self.val = val
59+
self.prev = prev
60+
self.next = next
61+
self.count = 0
62+
63+
64+
class LFUCache:
65+
66+
def __init__(self, capacity: int):
67+
self.capacity = capacity
68+
self.cache = {}
69+
self.headtails = {}
70+
self.hair = ListNode()
71+
72+
def get(self, key: int) -> int:
73+
if key not in self.cache:
74+
return -1
75+
76+
node = self.cache[key]
77+
head, tail = self.headtails[node.count]
78+
if head.key == node.key and tail.key == node.key:
79+
self.headtails.pop(node.count)
80+
elif head.key == node.key:
81+
self.headtails[node.count][0] = node.next
82+
elif tail.key == node.key:
83+
self.headtails[node.count][1] = node.prev
84+
node.prev.next = node.next
85+
if node.next is not None:
86+
node.next.prev = node.prev
87+
88+
node.count += 1
89+
if tail.next is None or tail.next.count > node.count:
90+
if tail.key == node.key:
91+
tail = node.prev
92+
self.headtails[node.count] = [node, node]
93+
else:
94+
tail = self.headtails[node.count][1]
95+
self.headtails[node.count][1] = node
96+
node.prev = tail
97+
node.next = tail.next
98+
tail.next = node
99+
if node.next is not None:
100+
node.next.prev = node
101+
102+
return node.val
103+
104+
def put(self, key: int, value: int) -> None:
105+
if key not in self.cache:
106+
if len(self.cache) == self.capacity:
107+
head, tail = self.headtails[self.hair.next.count]
108+
self.cache.pop(head.key)
109+
if head.key == tail.key:
110+
self.headtails.pop(head.count)
111+
else:
112+
self.headtails[head.count][0] = head.next
113+
self.hair.next = head.next
114+
if head.next is not None:
115+
head.next.prev = self.hair
116+
117+
node = ListNode(key, value, self.hair, self.hair.next)
118+
self.cache[key] = node
119+
self.headtails[0] = [node, node]
120+
self.hair.next = node
121+
if node.next is not None:
122+
node.next.prev = node
123+
124+
self.cache[key].val = value
125+
self.get(key)
126+
127+
128+
# Your LFUCache object will be instantiated and called as such:
129+
# obj = LFUCache(capacity)
130+
# param_1 = obj.get(key)
131+
# obj.put(key,value)
132+
```

Problemset/0460-LFU Cache/Solution.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
class ListNode:
2+
3+
def __init__(self, key=-1, val=0, prev=None, next=None):
4+
self.key = key
5+
self.val = val
6+
self.prev = prev
7+
self.next = next
8+
self.count = 0
9+
10+
11+
class LFUCache:
12+
13+
def __init__(self, capacity: int):
14+
self.capacity = capacity
15+
self.cache = {}
16+
self.headtails = {}
17+
self.hair = ListNode()
18+
19+
def get(self, key: int) -> int:
20+
if key not in self.cache:
21+
return -1
22+
23+
node = self.cache[key]
24+
head, tail = self.headtails[node.count]
25+
if head.key == node.key and tail.key == node.key:
26+
self.headtails.pop(node.count)
27+
elif head.key == node.key:
28+
self.headtails[node.count][0] = node.next
29+
elif tail.key == node.key:
30+
self.headtails[node.count][1] = node.prev
31+
node.prev.next = node.next
32+
if node.next is not None:
33+
node.next.prev = node.prev
34+
35+
node.count += 1
36+
if tail.next is None or tail.next.count > node.count:
37+
if tail.key == node.key:
38+
tail = node.prev
39+
self.headtails[node.count] = [node, node]
40+
else:
41+
tail = self.headtails[node.count][1]
42+
self.headtails[node.count][1] = node
43+
node.prev = tail
44+
node.next = tail.next
45+
tail.next = node
46+
if node.next is not None:
47+
node.next.prev = node
48+
49+
return node.val
50+
51+
def put(self, key: int, value: int) -> None:
52+
if key not in self.cache:
53+
if len(self.cache) == self.capacity:
54+
head, tail = self.headtails[self.hair.next.count]
55+
self.cache.pop(head.key)
56+
if head.key == tail.key:
57+
self.headtails.pop(head.count)
58+
else:
59+
self.headtails[head.count][0] = head.next
60+
self.hair.next = head.next
61+
if head.next is not None:
62+
head.next.prev = self.hair
63+
64+
node = ListNode(key, value, self.hair, self.hair.next)
65+
self.cache[key] = node
66+
self.headtails[0] = [node, node]
67+
self.hair.next = node
68+
if node.next is not None:
69+
node.next.prev = node
70+
71+
self.cache[key].val = value
72+
self.get(key)
73+
74+
75+
# Your LFUCache object will be instantiated and called as such:
76+
# obj = LFUCache(capacity)
77+
# param_1 = obj.get(key)
78+
# obj.put(key,value)

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@
331331
[455][455l] |[Assign Cookies][455] |![rb]&nbsp;&nbsp;![rs]
332332
[456][456l] |[132 Pattern][456] |![rs]
333333
[459][459l] |[Repeated Substring Pattern][459] |![py]
334+
[460][460l] |[LFU Cache][460] |![py]
334335
[461][461l] |[Hamming Distance][461] |![rs]
335336
[462][462l] |[Minimum Moves to Equal Array Elements II][462] |![rs]
336337
[463][463l] |[Island Perimeter][463] |![rs]
@@ -2044,6 +2045,7 @@
20442045
[455]:Problemset/0455-Assign%20Cookies/README.md#455-assign-cookies
20452046
[456]:Problemset/0456-132%20Pattern/README.md#456-132-pattern
20462047
[459]:Problemset/0459-Repeated%20Substring%20Pattern/README.md#459-repeated-substring-pattern
2048+
[460]:Problemset/0460-LFU%20Cache/README.md#460-lfu-cache
20472049
[461]:Problemset/0461-Hamming%20Distance/README.md#461-hamming-distance
20482050
[462]:Problemset/0462-Minimum%20Moves%20to%20Equal%20Array%20Elements%20II/README.md#462-minimum-moves-to-equal-array-elements-ii
20492051
[463]:Problemset/0463-Island%20Perimeter/README.md#463-island-perimeter
@@ -3751,6 +3753,7 @@
37513753
[455l]:https://leetcode.com/problems/assign-cookies/
37523754
[456l]:https://leetcode.com/problems/132-pattern/
37533755
[459l]:https://leetcode.com/problems/repeated-substring-pattern/
3756+
[460l]:https://leetcode.com/problems/lfu-cache/
37543757
[461l]:https://leetcode.com/problems/hamming-distance/
37553758
[462l]:https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/
37563759
[463l]:https://leetcode.com/problems/island-perimeter/

README_CN.md

+3
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@
331331
[455][455l] |[分发饼干][455] |![rb]&nbsp;&nbsp;![rs]
332332
[456][456l] |[132 模式][456] |![rs]
333333
[459][459l] |[重复的子字符串][459] |![py]
334+
[460][460l] |[LFU 缓存][460] |![py]
334335
[461][461l] |[汉明距离][461] |![rs]
335336
[462][462l] |[最少移动次数使数组元素相等 II][462] |![rs]
336337
[463][463l] |[岛屿的周长][463] |![rs]
@@ -2044,6 +2045,7 @@
20442045
[455]:Problemset/0455-Assign%20Cookies/README_CN.md#455-分发饼干
20452046
[456]:Problemset/0456-132%20Pattern/README_CN.md#456-132-模式
20462047
[459]:Problemset/0459-Repeated%20Substring%20Pattern/README_CN.md#459-重复的子字符串
2048+
[460]:Problemset/0460-LFU%20Cache/README_CN.md#460-lfu-缓存
20472049
[461]:Problemset/0461-Hamming%20Distance/README_CN.md#461-汉明距离
20482050
[462]:Problemset/0462-Minimum%20Moves%20to%20Equal%20Array%20Elements%20II/README_CN.md#462-最少移动次数使数组元素相等-ii
20492051
[463]:Problemset/0463-Island%20Perimeter/README_CN.md#463-岛屿的周长
@@ -3751,6 +3753,7 @@
37513753
[455l]:https://leetcode.cn/problems/assign-cookies/
37523754
[456l]:https://leetcode.cn/problems/132-pattern/
37533755
[459l]:https://leetcode.cn/problems/repeated-substring-pattern/
3756+
[460l]:https://leetcode.cn/problems/lfu-cache/
37543757
[461l]:https://leetcode.cn/problems/hamming-distance/
37553758
[462l]:https://leetcode.cn/problems/minimum-moves-to-equal-array-elements-ii/
37563759
[463l]:https://leetcode.cn/problems/island-perimeter/

0 commit comments

Comments
 (0)