- LFU:least frequently used最不经常使用
- LFU比LRU难一点,加入了使用量的概念,需要维护使用量的计数器。
- 题目:
当缓存达到其容量时,剔除使用次数最少的节点。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最久未使用的键。
思路:
- 没想通,结果直接看了答案...
- 开始的想法:kv存储,肯定需要map,但是和LRU不同,想着维护一个数组,下标代表使用次数,嵌套List,put时用头插法,每次清除最小下标List的尾节点即可,但如果访问量大数组会空余出很多节点。
- 看了答案(官方):
我们定义两个哈希表,第一个 freq_table 以频率 freq 为索引,每个索引存放一个双向链表,这个链表里存放所有使用频率为 freq 的缓存,缓存里存放三个信息,分别为键 key,值 value,以及使用频率 freq。第二个 key_table 以键值 key 为索引,每个索引存放对应缓存在 freq_table 中链表里的内存地址,这样我们就能利用两个哈希表来使得两个操作的时间复杂度均为 O(1)O(1)。同时需要记录一个当前缓存最少使用的频率 minFreq,这是为了删除操作服务的。
- 其他就是维护了一个最小使用的key,key的尾节点就是待删除节点。
// 官方代码,直接贴了...
class LFUCache {
int minfreq, capacity;
Map<Integer, Node> key_table;
Map<Integer, LinkedList<Node>> freq_table;
public LFUCache(int capacity) {
this.minfreq = 0;
this.capacity = capacity;
key_table = new HashMap<Integer, Node>();;
freq_table = new HashMap<Integer, LinkedList<Node>>();
}
public int get(int key) {
if (capacity == 0) {
return -1;
}
if (!key_table.containsKey(key)) {
return -1;
}
Node node = key_table.get(key);
int val = node.val, freq = node.freq;
freq_table.get(freq).remove(node);
// 如果当前链表为空,我们需要在哈希表中删除,且更新minFreq
if (freq_table.get(freq).size() == 0) {
freq_table.remove(freq);
if (minfreq == freq) {
minfreq += 1;
}
}
// 插入到 freq + 1 中
LinkedList<Node> list = freq_table.getOrDefault(freq + 1, new LinkedList<Node>());
list.offerFirst(new Node(key, val, freq + 1));
freq_table.put(freq + 1, list);
key_table.put(key, freq_table.get(freq + 1).peekFirst());
return val;
}
public void put(int key, int value) {
if (capacity == 0) {
return;
}
if (!key_table.containsKey(key)) {
// 缓存已满,需要进行删除操作
if (key_table.size() == capacity) {
// 通过 minFreq 拿到 freq_table[minFreq] 链表的末尾节点
Node node = freq_table.get(minfreq).peekLast();
key_table.remove(node.key);
freq_table.get(minfreq).pollLast();
if (freq_table.get(minfreq).size() == 0) {
freq_table.remove(minfreq);
}
}
LinkedList<Node> list = freq_table.getOrDefault(1, new LinkedList<Node>());
list.offerFirst(new Node(key, value, 1));
freq_table.put(1, list);
key_table.put(key, freq_table.get(1).peekFirst());
minfreq = 1;
} else {
// 与 get 操作基本一致,除了需要更新缓存的值
Node node = key_table.get(key);
int freq = node.freq;
freq_table.get(freq).remove(node);
if (freq_table.get(freq).size() == 0) {
freq_table.remove(freq);
if (minfreq == freq) {
minfreq += 1;
}
}
LinkedList<Node> list = freq_table.getOrDefault(freq + 1, new LinkedList<Node>());
list.offerFirst(new Node(key, value, freq + 1));
freq_table.put(freq + 1, list);
key_table.put(key, freq_table.get(freq + 1).peekFirst());
}
}
}
class Node {
int key, val, freq;
Node(int key, int val, int freq) {
this.key = key;
this.val = val;
this.freq = freq;
}
}
网友评论