题目
请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。
实现 LFUCache 类:
- LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
- int get(int key) - 如果键存在于缓存中,则获取键的值,否则返回 -1。
- void put(int key, int value) - 如果键已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最久未使用 的键。
注意「项的使用次数」就是自插入该项以来对其调用 get 和 put 函数的次数之和。使用次数会在对应项被移除后置为 0 。
进阶:
你是否可以在 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]
解释:
LFUCache lFUCache = new LFUCache(2);
lFUCache.put(1, 1);
lFUCache.put(2, 2);
lFUCache.get(1); // 返回 1
lFUCache.put(3, 3); // 去除键 2
lFUCache.get(2); // 返回 -1(未找到)
lFUCache.get(3); // 返回 3
lFUCache.put(4, 4); // 去除键 1
lFUCache.get(1); // 返回 -1(未找到)
lFUCache.get(3); // 返回 3
lFUCache.get(4); // 返回 4
提示:
- 0 <= capacity, key, value <= 104
- 最多调用 105 次 get 和 put 方法
实现
package lfu;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
public class LfuCache {
public static void main(String[] args) {
LfuCache cache = new LfuCache (2);
cache.put("a", "1");
cache.put("b", "2");
cache.put("c", "3");
System.out.println(cache.toString());
System.out.println(cache.get("a"));
System.out.println(cache.get("b"));
cache.put("d", "4");
System.out.println(cache.toString());
}
private int capacity;
private int minFreq = 0;
private Map<String, Node> key2Node;
private Map<Integer, LinkedList<Node>> freq2Nodes;
public LfuCache(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("capacity can not le 0");
}
this.capacity = capacity;
this.key2Node = new HashMap<>(capacity);
this.freq2Nodes = new HashMap<>(capacity);
}
public String get(String key) {
if (key == null) {
throw new IllegalArgumentException("key can not be null");
}
Node node = key2Node.get(key);
if (null == node) {
return null;
}
resetPutNode(node);
return node.value;
}
public void put(String key, String value) {
if (key == null || value == key) {
throw new IllegalArgumentException("key/value can not be null");
}
if (key2Node.containsKey(key)) {
Node node = key2Node.get(key);
node.value = value;
resetPutNode(node);
} else {
if (key2Node.size() == capacity) {
Node node = freq2Nodes.get(minFreq).removeFirst();
key2Node.remove(node.key);
}
minFreq = 1;
putNode(new Node(key, value));
}
}
/**
* 1、先进行原来 key2Node 及 freq2Nodes 的删除,如果 等于 minFreq 情况,需要minFreq++
* 2、重新将 node 添加到 key2Node 及 freq2Nodes
*/
private void resetPutNode(Node node) {
LinkedList<Node> list = freq2Nodes.get(node.freq);
if (list.size() == 1 && node.freq == minFreq) {
minFreq++;
}
list.remove(node);
putNode(node);
}
/**
* 进行node。freq添加,然后添加到key2Node 和 count2Node 里面
*/
private void putNode(Node node) {
node.freq++;
key2Node.put(node.key, node);
LinkedList<Node> list = freq2Nodes.getOrDefault(node.freq, new LinkedList<>());
list.addLast(node);
freq2Nodes.put(node.freq, list);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("capacity = ").append(capacity).append("\n");
sb.append("key2Node = ").append(key2Node).append("\n");
sb.append("freq2Nodes = ").append(freq2Nodes).append("\n");
return sb.toString();
}
private static class Node {
public Node(String key, String value) {
this.key = key;
this.value = value;
}
String key;
String value;
int freq;
@Override
public String toString(){
StringBuilder sb = new StringBuilder("(");
sb.append("key:").append(key).append(",");
sb.append("value:").append(value).append(",");
sb.append("freq:").append(freq).append(")");
return sb.toString();
}
}
}