题目链接
LFU 缓存
题目描述


注意点
- 1 <= capacity <= 10^4
- 0 <= key <= 10^5
- 0 <= value <= 10^9
- 对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增
- 当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项
- 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行
解答思路
- 创建Node双向链表节点类,其中包含freq,key,value,prev指针,next指针
- 两个Map,kvMap存储键值对,freqMap存储频率以及对应频率的链表头尾节点,capacity存储容量,minFreq存储最小调用频率
- 做get()操作时,如果key无相应节点,直接返回-1;如果有相应节点,则其频率要加一,要从原频率链表中移除该节点,并将该节点加入到新频率的链表中,还要注意更新minFreq的值
- 做put()操作时
- 如果key有相应节点,则取出原有节点,将原有节点取出,将其频率加一,从原频率链表中移除该节点,并将该节点加入到新频率的链表中,还要注意更新minFreq的值
- 如果key无相应节点,则需要新建一个节点,写入key,value,freq为1,再将该节点加入到kvMap和freqMap对应频率链表中,还要判断此时kvMap是否已满,如果节点过多还要移除最不经常使用的节点,也就是最低频率minFreq对应链表中的第一个节点,还要注意更新minFreq的值为1
代码
class LFUCache {
int minFreq;
int capacity;
Map<Integer, Node> kvMap;
Map<Integer, Node[]> freqMap;
public LFUCache(int capacity) {
minFreq = 0;
this.capacity = capacity;
kvMap = new HashMap<>(capacity);
freqMap = new HashMap<>();
}
public int get(int key) {
if (kvMap.get(key) == null) {
return -1;
}
Node node = kvMap.get(key);
if (node.freq == minFreq && node.prev.prev == null && node.next.next == null) {
minFreq++;
}
node.freq++;
deleteNode(node);
if (freqMap.get(node.freq) == null) {
initFreqMap(node.freq);
}
addTail(node, freqMap.get(node.freq)[1]);
return node.value;
}
public void put(int key, int value) {
Node node = new Node();
if (kvMap.get(key) != null) {
node = kvMap.get(key);
if (node.freq == minFreq && node.prev.prev == null && node.next.next == null) {
minFreq++;
}
node.freq++;
node.value = value;
deleteNode(node);
} else {
if (capacity == 0) {
Node head = freqMap.get(minFreq)[0];
kvMap.remove(head.next.key);
deleteNode(head.next);
capacity = 1;
}
node.freq = 1;
node.key = key;
node.value = value;
kvMap.put(key, node);
minFreq = 1;
capacity--;
}
if (freqMap.get(node.freq) == null) {
initFreqMap(node.freq);
}
addTail(node, freqMap.get(node.freq)[1]);
}
public void initFreqMap(int freq) {
Node head = new Node();
Node tail = new Node();
head.next = tail;
tail.prev = head;
Node[] arr = new Node[] {head, tail};
freqMap.put(freq, arr);
}
public void deleteNode(Node node) {
Node prevNode = node.prev;
Node nextNode = node.next;
prevNode.next = nextNode;
nextNode.prev = prevNode;
node.prev = null;
node.next = null;
}
public void addTail(Node node, Node tail) {
Node prevNode = tail.prev;
prevNode.next = node;
node.prev = prevNode;
node.next = tail;
tail.prev = node;
}
}
class Node {
int freq;
int key;
int value;
Node prev;
Node next;
}
关键点
- 注意更新minFreq的值
- 双向链表的相关操作
- 容量满时插入节点需要对使用频率最低的节点做删除操作