LFU算法
LFU算法逻辑:
1、调用get(key)方法时,要返回该key对应的val。
2、只要用get或者put方法访问一次某个key,该key的freq就要加一。
3、如果在容量满了的时候进行插入,则需要将freq最小的key删除,如果最小的freq对应多个key,则删除其中最旧的那一个。
我们希望能够在 O(1) 的时间内解决这些需求,可以使用基本数据结构来逐个击破:
1、使用一个HashMap存储key到val的映射,就可以快速计算get(key)。
HashMap<Integer, Integer> keyToVal;
2、使用一个HashMap存储key到freq的映射,就可以快速操作key对应的freq。
HashMap<Integer, Integer> keyToFreq;
3、这个需求应该是 LFU 算法的核心,所以我们分开说。
3.1、首先,肯定是需要freq到key的映射,用来找到freq最小的key。
3.2、将freq最小的key删除,那你就得快速得到当前所有key最小的freq是多少。想要时间复杂度 O(1) 的话,肯定不能遍历一遍去找,那就用一个变量minFreq来记录当前最小的freq吧。
3.3、可能有多个key拥有相同的freq,所以 freq对key是一对多的关系,即一个freq对应一个key的列表。
3.4、希望freq对应的key的列表是存在时序的,便于快速查找并删除最旧的key。
3.5、希望能够快速删除key列表中的任何一个key,因为如果频次为freq的某个key被访问,那么它的频次就会变成freq+1,就应该从freq对应的key列表中删除,加到freq+1对应的key的列表中。
HashMap<Integer, LinkedHashSet<Integer>> freqToKeys;
int minFreq = 0;
LFU算法的基本数据结构:
class LFUCache {
// key 到 val 的映射,我们后文称为 KV 表
HashMap<Integer, Integer> keyToVal;
// key 到 freq 的映射,我们后文称为 KF 表
HashMap<Integer, Integer> keyToFreq;
// freq 到 key 列表的映射,我们后文称为 FK 表
HashMap<Integer, LinkedHashSet<Integer>> freqToKeys;
// 记录最小的频次
int minFreq;
// 记录 LFU 缓存的最大容量
int cap;
public LFUCache(int capacity) {
keyToVal = new HashMap<>();
keyToFreq = new HashMap<>();
freqToKeys = new HashMap<>();
this.cap = capacity;
this.minFreq = 0;
}
public int get(int key) {
}
public void put(int key, int val) {
}
}
代码框架: