460. LFU Cache #
题目 #
为 最不经常使用(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)
的平均时间复杂度运行。
思路 #
代码 #
class LFUCache {
private class ListNode {
public int key;
public int frequency;
public int val;
public ListNode prev;
public ListNode next;
ListNode(int key, int value, ListNode prev, ListNode next) {
this.key = key;
this.val = value;
this.frequency = 0;
this.prev = prev;
this.next = next;
}
}
private int size;
private int capacity;
private ListNode sentinel;
private Map<Integer, ListNode> keyMap;
private Map<Integer, ListNode> freqMap;
public int get(int key) {
if (this.capacity == 0) return -1;
if (this.keyMap.containsKey(key) == false) return -1;
ListNode node = this.keyMap.get(key);
/** 由于 get 操作,node.frequency 递增,修订 freqMap */
refresh(node);
return node.val;
}
public void put(int key, int value) {
if (this.keyMap.containsKey(key) == true) {
ListNode node = this.keyMap.get(key);
node.val = value;
refresh(node);
return;
}
else if (this.size == this.capacity) {
/** 在 keyMap 中移除 LRU 结点 */
this.keyMap.remove(this.sentinel.prev.key);
/** 若 LRU 结点是 LRU.frequency 的独苗,则在freqMap中移除 LRU 结点 */
if (this.sentinel.prev == this.freqMap.get(this.sentinel.prev.frequency)) this.freqMap.remove(this.sentinel.prev.frequency);
/** 移除 LRU 结点 */
this.sentinel.prev = this.sentinel.prev.prev;
this.sentinel.prev.next = this.sentinel;
}
ListNode node = new ListNode(key, value, this.sentinel.prev, this.sentinel);
node.prev.next = node;
node.next.prev = node;
this.keyMap.put(key, node);
refresh(node);
if (this.size < this.capacity) this.size += 1;
}
public void refresh(ListNode node) {
/** 变更 node.frequency 在 freqMap 中的映射 */
if (node.frequency != 0) {
ListNode frontier = this.freqMap.get(node.frequency);
if (node == frontier && node.frequency == node.next.frequency) this.freqMap.put(node.frequency, node.next);
else if (node == frontier && node.frequency != node.next.frequency) this.freqMap.remove(node.frequency);
else {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = frontier.prev;
node.next = frontier;
node.prev.next = node;
node.next.prev = node;
}
}
/** 变更 node.frequency + 1 在 freqMap 中的映射 */
node.frequency += 1;
if (this.freqMap.containsKey(node.frequency) == true) {
ListNode frontier = this.freqMap.get(node.frequency);
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = frontier.prev;
node.next = frontier;
node.prev.next = node;
node.next.prev = node;
}
this.freqMap.put(node.frequency, node);
}
}