为了实现LFU,对于每个页面,不仅需要记录访问时间的次序(用双向链表),还要记录访问频率(用HashMap,key是频率,value是这个频率下所有页面组成的双向链表),同时还有随机读写(用哈希表存储组织每个页面),所有一共有两个哈希表。
下图(来源leetcode题解https://leetcode-cn.com/problems/lfu-cache/solution/lfuhuan-cun-by-leetcode-solution/)解释了链表和哈希表组织方式:
class LFUCache {
int capacity;
int minfre;//当前最小的层数
int cur;
HashMap<Integer,doubleList> fre;//分层链表的map
HashMap<Integer,Node> map1;//储存每一元素的map
public LFUCache(int capacity) {
this.capacity=capacity;
fre = new HashMap<>();
map1 = new HashMap<>();
this.cur=0;
}
public int get(int key) {
if(capacity==0) return-1;
if(cur==0) return -1;
if(map1.containsKey(key)){
Node node=map1.get(key);//获取当前元素
doubleList list = fre.get(node.fre);//获取元素所在的层
if(list.size==1){//如果这一层只有一个元素 就删除整个层
fre.remove(node.fre);
if(minfre==node.fre) minfre++;//更新最小层minfre 此元素的层数必然会上升一层,所以minfre +1 必然
//是新的最小层
node.fre++;
if(fre.containsKey(node.fre)){ // 把元素加入新的层
doubleList list1 = fre.get(node.fre);
list1.add(node);
}
else{
doubleList list2 =new doubleList();//如果当前层没有初始化就初始化
list2.add(node);
fre.put(node.fre,list2);
}
}
else{
node.fre++;
list.remove(node);
if(fre.containsKey(node.fre)){// 把元素加入新的层
doubleList list1 = fre.get(node.fre);
list1.add(node);
}
else{
doubleList list2 =new doubleList();
list2.add(node);
fre.put(node.fre,list2);
}
}
return node.val;
}
else return -1;
}
public void put(int key, int value) {;
if(capacity==0) return;
if(map1.containsKey(key)){
Node node=map1.get(key);
node.val=value;
get(key);
}
else{
if(cur==capacity){ // 容量满了 就删除最小层的第一个元素
doubleList list = fre.get(minfre);
int thekey=list.first.next.key;
if(list.size==1){
fre.remove(minfre);//删除整个层,因为这层空了
list=null;
}
else{
list.removeFirst();
}
map1.remove(thekey);
cur--;
}
Node node = new Node(key,value);
map1.put(key,node);
if(fre.containsKey(1)){ // 新元素 插入第一层
doubleList list = fre.get(1);
list.add(node);
}
else{
doubleList list = new doubleList();
list.add(node);
fre.put(1,list);
}
cur++;
minfre=1;//更新最小层
}
}
}
class Node{
int fre;
int val;
int key;
Node pre;
Node next;
public Node(int key,int value){
this.fre=1;
this.val=value;
this.key=key;
}
public Node(){
val=11;
}
}
class doubleList{//双向链表
int size;
Node first;
Node last;
public doubleList(){
size=0;
first=new Node();
last=new Node();
last.pre=first;
first.next=last;
}
public void add(Node node){
node.pre=last.pre;
last.pre.next=node;
node.next=last;
last.pre=node;
size++;
}
public void removeFirst(){
first.next.next.pre=first;
first.next=first.next.next;
size--;
}
public void remove(Node node){
node.pre.next=node.next;
node.next.pre=node.pre;
size--;
node=null;
}
}