友情支持
如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜
有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。
公众号的微信号是: jikerizhi 。因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。 |
460. LFU 缓存
请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。
实现 LFUCache
类:
-
LFUCache(int capacity)
- 用数据结构的容量capacity
初始化对象 -
int get(int key)
- 如果键key
存在于缓存中,则获取键的值,否则返回-1
。 -
void put(int key, int value)
- 如果键key
已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量capacity
时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最久未使用 的键。
为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器。使用计数最小的键是最久未使用的键。
当一个键首次插入到缓存中时,它的使用计数器被设置为 1
(由于 put 操作)。对缓存中的键执行 get
或 put
操作,使用计数器的值将会递增。
函数 get
和 put
必须以 O(1)
的平均时间复杂度运行。
示例:
输入: ["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]] 输出: [null, null, null, 1, null, -1, 3, null, -1, 3, 4] 解释: // cnt(x) = 键 x 的使用计数 // cache=[] 将显示最后一次使用的顺序(最左边的元素是最近的) LFUCache lfu = new LFUCache(2); lfu.put(1, 1); // cache=[1,_], cnt(1)=1 lfu.put(2, 2); // cache=[2,1], cnt(2)=1, cnt(1)=1 lfu.get(1); // 返回 1 // cache=[1,2], cnt(2)=1, cnt(1)=2 lfu.put(3, 3); // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小 // cache=[3,1], cnt(3)=1, cnt(1)=2 lfu.get(2); // 返回 -1(未找到) lfu.get(3); // 返回 3 // cache=[3,1], cnt(3)=2, cnt(1)=2 lfu.put(4, 4); // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用 // cache=[4,3], cnt(4)=1, cnt(3)=2 lfu.get(1); // 返回 -1(未找到) lfu.get(3); // 返回 3 // cache=[3,4], cnt(4)=1, cnt(3)=3 lfu.get(4); // 返回 4 // cache=[3,4], cnt(4)=2, cnt(3)=3
提示:
-
1 <= capacity <= 104
-
0 <= key <= 105
-
0 <= value <= 109
-
最多调用
2 * 105
次get
和put
方法
思路分析
使用两个 Map
,一个存数据,另外一个存频率到头节点的关联关系。代码还没调试通!明天继续!
-
一刷
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
/**
* @author D瓜哥 · https://www.diguage.com
* @since 2025-08-26 22:05:19
*/
static class LFUCache {
private int capacity;
private Map<Integer, Node> keyToData;
private final Node HEAD;
private final Node TAIL;
private Map<Integer, Node> countToNode;
public LFUCache(int capacity) {
this.capacity = capacity;
this.keyToData = new HashMap<>();
countToNode = new HashMap<>();
HEAD = new Node(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
TAIL = new Node(Integer.MIN_VALUE, Integer.MIN_VALUE, 1);
HEAD.next = TAIL;
TAIL.prev = HEAD;
countToNode.put(1, TAIL);
}
public int get(int key) {
Node node = keyToData.get(key);
if (node == null) {
return -1;
}
updateList(node);
return node.value;
}
public void put(int key, int value) {
Node node = keyToData.get(key);
if (node == null) {
if (keyToData.size() == capacity) {
deleteNode(TAIL.prev);
}
node = new Node(key, value);
}
node.value = value;
updateList(node);
}
private void updateList(Node node) {
Node prev = getPrevNode(node);
if (node.next != null) {
Node next = node.next;
if (next.count == node.count) {
countToNode.put(next.count, next);
} else {
countToNode.remove(node.count);
}
} else {
countToNode.remove(node.count);
}
putAfter(prev, node);
}
private Node getPrevNode(Node node) {
Node next = countToNode.get(node.count + 1);
if (next == null) {
next = countToNode.get(node.count);
}
return next.prev;
}
private void deleteNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
keyToData.remove(node.key);
if (node.next != null) {
if (node.next.count == node.count) {
countToNode.put(node.next.count, node.next);
} else {
countToNode.remove(node.count);
}
}
}
private void putAfter(Node prev, Node node) {
node.count++;
node.time = System.currentTimeMillis();
if (node.prev != null) {
node.prev.next = node.next;
}
if (node.next != null) {
node.next.prev = node.prev;
}
node.prev = prev;
Node next = prev.next;
node.next = next;
next.prev = node;
prev.next = node;
keyToData.put(node.key, node);
countToNode.put(node.count, node);
}
private static class Node {
int key;
int value;
long time;
int count;
Node prev;
Node next;
public Node(int key, int value) {
this(key, value, 0);
}
public Node(int key, int value, int count) {
this.key = key;
this.value = value;
this.count = count;
this.time = System.currentTimeMillis();
}
}
}